fixed broken player option "no centering when relocating"
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 // DEBUG SETTINGS
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 // EXPERIMENTAL STUFF
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 // EXPERIMENTAL STUFF
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 // for DigField()
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 // for MovePlayer()
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 // for ScrollPlayer()
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 // for Bang()/Explode()
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 // game panel display and control definitions
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 // values for delayed check of falling and moving elements and for collision
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 // values for initial player move delay (initial delay counter value)
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 // values for player movement speed (which is in fact a delay value)
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 // values for scroll positions
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 // values for other actions
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
883                                  RND(element_info[e].step_delay_random))
884 #define GET_MAX_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
885                                     (element_info[e].step_delay_random))
886 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
887                                  RND(element_info[e].ce_value_random_initial))
888 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
889 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
890                                  RND((c)->delay_random * (c)->delay_frames))
891 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
892                                  RND((c)->delay_random))
893
894
895 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
896          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
897
898 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
899         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
900          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
901          (be) + (e) - EL_SELF)
902
903 #define GET_PLAYER_FROM_BITS(p)                                         \
904         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
905
906 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
907         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
908          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
909          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
910          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
911          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
912          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
913          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
914          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
915          (e))
916
917 #define CAN_GROW_INTO(e)                                                \
918         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (condition)))
923
924 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
925                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
926                                         (CAN_MOVE_INTO_ACID(e) &&       \
927                                          Tile[x][y] == EL_ACID) ||      \
928                                         (condition)))
929
930 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
931                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
932                                         (CAN_MOVE_INTO_ACID(e) &&       \
933                                          Tile[x][y] == EL_ACID) ||      \
934                                         (condition)))
935
936 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
937                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
938                                         (condition) ||                  \
939                                         (CAN_MOVE_INTO_ACID(e) &&       \
940                                          Tile[x][y] == EL_ACID) ||      \
941                                         (DONT_COLLIDE_WITH(e) &&        \
942                                          IS_PLAYER(x, y) &&             \
943                                          !PLAYER_ENEMY_PROTECTED(x, y))))
944
945 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
946         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
947
948 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
949         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
950
951 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
952         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Tile[x][y] == EL_EMC_PLANT)
953
954 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
955         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Tile[x][y]) || \
956                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
957
958 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
959         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
960
961 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
962         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Tile[x][y] == EL_DIAMOND)
963
964 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
965         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_FOOD_DARK_YAMYAM(Tile[x][y]))
966
967 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
968         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Tile[x][y]))
969
970 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
971         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Tile[x][y]))
972
973 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
974         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Tile[x][y] == EL_EXIT_OPEN || \
975                                                  Tile[x][y] == EL_EM_EXIT_OPEN || \
976                                                  Tile[x][y] == EL_STEEL_EXIT_OPEN || \
977                                                  Tile[x][y] == EL_EM_STEEL_EXIT_OPEN || \
978                                                  IS_FOOD_PENGUIN(Tile[x][y])))
979 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
980         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
981
982 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
983         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
984
985 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
986         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
987
988 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
989         (IN_LEV_FIELD(x, y) && (Tile[x][y] == EL_EMC_SPRING_BUMPER ||   \
990                                 Tile[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
991
992 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
993
994 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
995                 (!IS_PLAYER(x, y) &&                                    \
996                  IS_EQUAL_OR_IN_GROUP(Tile[x][y], MOVE_ENTER_EL(e)))
997
998 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
999         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1000
1001 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1002 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1003
1004 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1005 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1006 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1007 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1008
1009 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1010
1011 // game button identifiers
1012 #define GAME_CTRL_ID_STOP               0
1013 #define GAME_CTRL_ID_PAUSE              1
1014 #define GAME_CTRL_ID_PLAY               2
1015 #define GAME_CTRL_ID_UNDO               3
1016 #define GAME_CTRL_ID_REDO               4
1017 #define GAME_CTRL_ID_SAVE               5
1018 #define GAME_CTRL_ID_PAUSE2             6
1019 #define GAME_CTRL_ID_LOAD               7
1020 #define GAME_CTRL_ID_PANEL_STOP         8
1021 #define GAME_CTRL_ID_PANEL_PAUSE        9
1022 #define GAME_CTRL_ID_PANEL_PLAY         10
1023 #define GAME_CTRL_ID_TOUCH_STOP         11
1024 #define GAME_CTRL_ID_TOUCH_PAUSE        12
1025 #define SOUND_CTRL_ID_MUSIC             13
1026 #define SOUND_CTRL_ID_LOOPS             14
1027 #define SOUND_CTRL_ID_SIMPLE            15
1028 #define SOUND_CTRL_ID_PANEL_MUSIC       16
1029 #define SOUND_CTRL_ID_PANEL_LOOPS       17
1030 #define SOUND_CTRL_ID_PANEL_SIMPLE      18
1031
1032 #define NUM_GAME_BUTTONS                19
1033
1034
1035 // forward declaration for internal use
1036
1037 static void CreateField(int, int, int);
1038
1039 static void ResetGfxAnimation(int, int);
1040
1041 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1042 static void AdvanceFrameAndPlayerCounters(int);
1043
1044 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1045 static boolean MovePlayer(struct PlayerInfo *, int, int);
1046 static void ScrollPlayer(struct PlayerInfo *, int);
1047 static void ScrollScreen(struct PlayerInfo *, int);
1048
1049 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1050 static boolean DigFieldByCE(int, int, int);
1051 static boolean SnapField(struct PlayerInfo *, int, int);
1052 static boolean DropElement(struct PlayerInfo *);
1053
1054 static void InitBeltMovement(void);
1055 static void CloseAllOpenTimegates(void);
1056 static void CheckGravityMovement(struct PlayerInfo *);
1057 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1058 static void KillPlayerUnlessEnemyProtected(int, int);
1059 static void KillPlayerUnlessExplosionProtected(int, int);
1060
1061 static void CheckNextToConditions(int, int);
1062 static void TestIfPlayerNextToCustomElement(int, int);
1063 static void TestIfPlayerTouchesCustomElement(int, int);
1064 static void TestIfElementNextToCustomElement(int, int);
1065 static void TestIfElementTouchesCustomElement(int, int);
1066 static void TestIfElementHitsCustomElement(int, int, int);
1067
1068 static void HandleElementChange(int, int, int);
1069 static void ExecuteCustomElementAction(int, int, int, int);
1070 static boolean ChangeElement(int, int, int, int);
1071
1072 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int, int, int);
1073 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1074         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
1075 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1076         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1077 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1078         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1079 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1080         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1081 #define CheckTriggeredElementChangeByMouse(x, y, e, ev, s)              \
1082         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1083
1084 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1085 #define CheckElementChange(x, y, e, te, ev)                             \
1086         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1087 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1088         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1089 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1090         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1091 #define CheckElementChangeByMouse(x, y, e, ev, s)                       \
1092         CheckElementChangeExt(x, y, e, EL_UNDEFINED, ev, CH_PLAYER_ANY, s)
1093
1094 static void PlayLevelSound(int, int, int);
1095 static void PlayLevelSoundNearest(int, int, int);
1096 static void PlayLevelSoundAction(int, int, int);
1097 static void PlayLevelSoundElementAction(int, int, int, int);
1098 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1099 static void PlayLevelSoundActionIfLoop(int, int, int);
1100 static void StopLevelSoundActionIfLoop(int, int, int);
1101 static void PlayLevelMusic(void);
1102 static void FadeLevelSoundsAndMusic(void);
1103
1104 static void HandleGameButtons(struct GadgetInfo *);
1105
1106 int AmoebaNeighbourNr(int, int);
1107 void AmoebaToDiamond(int, int);
1108 void ContinueMoving(int, int);
1109 void Bang(int, int);
1110 void InitMovDir(int, int);
1111 void InitAmoebaNr(int, int);
1112 void NewHighScore(int, boolean);
1113
1114 void TestIfGoodThingHitsBadThing(int, int, int);
1115 void TestIfBadThingHitsGoodThing(int, int, int);
1116 void TestIfPlayerTouchesBadThing(int, int);
1117 void TestIfPlayerRunsIntoBadThing(int, int, int);
1118 void TestIfBadThingTouchesPlayer(int, int);
1119 void TestIfBadThingRunsIntoPlayer(int, int, int);
1120 void TestIfFriendTouchesBadThing(int, int);
1121 void TestIfBadThingTouchesFriend(int, int);
1122 void TestIfBadThingTouchesOtherBadThing(int, int);
1123 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1124
1125 void KillPlayer(struct PlayerInfo *);
1126 void BuryPlayer(struct PlayerInfo *);
1127 void RemovePlayer(struct PlayerInfo *);
1128 void ExitPlayer(struct PlayerInfo *);
1129
1130 static int getInvisibleActiveFromInvisibleElement(int);
1131 static int getInvisibleFromInvisibleActiveElement(int);
1132
1133 static void TestFieldAfterSnapping(int, int, int, int, int);
1134
1135 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1136
1137 // for detection of endless loops, caused by custom element programming
1138 // (using maximal playfield width x 10 is just a rough approximation)
1139 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1140
1141 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1142 {                                                                       \
1143   if (recursion_loop_detected)                                          \
1144     return (rc);                                                        \
1145                                                                         \
1146   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1147   {                                                                     \
1148     recursion_loop_detected = TRUE;                                     \
1149     recursion_loop_element = (e);                                       \
1150   }                                                                     \
1151                                                                         \
1152   recursion_loop_depth++;                                               \
1153 }
1154
1155 #define RECURSION_LOOP_DETECTION_END()                                  \
1156 {                                                                       \
1157   recursion_loop_depth--;                                               \
1158 }
1159
1160 static int recursion_loop_depth;
1161 static boolean recursion_loop_detected;
1162 static boolean recursion_loop_element;
1163
1164 static int map_player_action[MAX_PLAYERS];
1165
1166
1167 // ----------------------------------------------------------------------------
1168 // definition of elements that automatically change to other elements after
1169 // a specified time, eventually calling a function when changing
1170 // ----------------------------------------------------------------------------
1171
1172 // forward declaration for changer functions
1173 static void InitBuggyBase(int, int);
1174 static void WarnBuggyBase(int, int);
1175
1176 static void InitTrap(int, int);
1177 static void ActivateTrap(int, int);
1178 static void ChangeActiveTrap(int, int);
1179
1180 static void InitRobotWheel(int, int);
1181 static void RunRobotWheel(int, int);
1182 static void StopRobotWheel(int, int);
1183
1184 static void InitTimegateWheel(int, int);
1185 static void RunTimegateWheel(int, int);
1186
1187 static void InitMagicBallDelay(int, int);
1188 static void ActivateMagicBall(int, int);
1189
1190 struct ChangingElementInfo
1191 {
1192   int element;
1193   int target_element;
1194   int change_delay;
1195   void (*pre_change_function)(int x, int y);
1196   void (*change_function)(int x, int y);
1197   void (*post_change_function)(int x, int y);
1198 };
1199
1200 static struct ChangingElementInfo change_delay_list[] =
1201 {
1202   {
1203     EL_NUT_BREAKING,
1204     EL_EMERALD,
1205     6,
1206     NULL,
1207     NULL,
1208     NULL
1209   },
1210   {
1211     EL_PEARL_BREAKING,
1212     EL_EMPTY,
1213     8,
1214     NULL,
1215     NULL,
1216     NULL
1217   },
1218   {
1219     EL_EXIT_OPENING,
1220     EL_EXIT_OPEN,
1221     29,
1222     NULL,
1223     NULL,
1224     NULL
1225   },
1226   {
1227     EL_EXIT_CLOSING,
1228     EL_EXIT_CLOSED,
1229     29,
1230     NULL,
1231     NULL,
1232     NULL
1233   },
1234   {
1235     EL_STEEL_EXIT_OPENING,
1236     EL_STEEL_EXIT_OPEN,
1237     29,
1238     NULL,
1239     NULL,
1240     NULL
1241   },
1242   {
1243     EL_STEEL_EXIT_CLOSING,
1244     EL_STEEL_EXIT_CLOSED,
1245     29,
1246     NULL,
1247     NULL,
1248     NULL
1249   },
1250   {
1251     EL_EM_EXIT_OPENING,
1252     EL_EM_EXIT_OPEN,
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258   {
1259     EL_EM_EXIT_CLOSING,
1260     EL_EMPTY,
1261     29,
1262     NULL,
1263     NULL,
1264     NULL
1265   },
1266   {
1267     EL_EM_STEEL_EXIT_OPENING,
1268     EL_EM_STEEL_EXIT_OPEN,
1269     29,
1270     NULL,
1271     NULL,
1272     NULL
1273   },
1274   {
1275     EL_EM_STEEL_EXIT_CLOSING,
1276     EL_STEELWALL,
1277     29,
1278     NULL,
1279     NULL,
1280     NULL
1281   },
1282   {
1283     EL_SP_EXIT_OPENING,
1284     EL_SP_EXIT_OPEN,
1285     29,
1286     NULL,
1287     NULL,
1288     NULL
1289   },
1290   {
1291     EL_SP_EXIT_CLOSING,
1292     EL_SP_EXIT_CLOSED,
1293     29,
1294     NULL,
1295     NULL,
1296     NULL
1297   },
1298   {
1299     EL_SWITCHGATE_OPENING,
1300     EL_SWITCHGATE_OPEN,
1301     29,
1302     NULL,
1303     NULL,
1304     NULL
1305   },
1306   {
1307     EL_SWITCHGATE_CLOSING,
1308     EL_SWITCHGATE_CLOSED,
1309     29,
1310     NULL,
1311     NULL,
1312     NULL
1313   },
1314   {
1315     EL_TIMEGATE_OPENING,
1316     EL_TIMEGATE_OPEN,
1317     29,
1318     NULL,
1319     NULL,
1320     NULL
1321   },
1322   {
1323     EL_TIMEGATE_CLOSING,
1324     EL_TIMEGATE_CLOSED,
1325     29,
1326     NULL,
1327     NULL,
1328     NULL
1329   },
1330
1331   {
1332     EL_ACID_SPLASH_LEFT,
1333     EL_EMPTY,
1334     8,
1335     NULL,
1336     NULL,
1337     NULL
1338   },
1339   {
1340     EL_ACID_SPLASH_RIGHT,
1341     EL_EMPTY,
1342     8,
1343     NULL,
1344     NULL,
1345     NULL
1346   },
1347   {
1348     EL_SP_BUGGY_BASE,
1349     EL_SP_BUGGY_BASE_ACTIVATING,
1350     0,
1351     InitBuggyBase,
1352     NULL,
1353     NULL
1354   },
1355   {
1356     EL_SP_BUGGY_BASE_ACTIVATING,
1357     EL_SP_BUGGY_BASE_ACTIVE,
1358     0,
1359     InitBuggyBase,
1360     NULL,
1361     NULL
1362   },
1363   {
1364     EL_SP_BUGGY_BASE_ACTIVE,
1365     EL_SP_BUGGY_BASE,
1366     0,
1367     InitBuggyBase,
1368     WarnBuggyBase,
1369     NULL
1370   },
1371   {
1372     EL_TRAP,
1373     EL_TRAP_ACTIVE,
1374     0,
1375     InitTrap,
1376     NULL,
1377     ActivateTrap
1378   },
1379   {
1380     EL_TRAP_ACTIVE,
1381     EL_TRAP,
1382     31,
1383     NULL,
1384     ChangeActiveTrap,
1385     NULL
1386   },
1387   {
1388     EL_ROBOT_WHEEL_ACTIVE,
1389     EL_ROBOT_WHEEL,
1390     0,
1391     InitRobotWheel,
1392     RunRobotWheel,
1393     StopRobotWheel
1394   },
1395   {
1396     EL_TIMEGATE_SWITCH_ACTIVE,
1397     EL_TIMEGATE_SWITCH,
1398     0,
1399     InitTimegateWheel,
1400     RunTimegateWheel,
1401     NULL
1402   },
1403   {
1404     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1405     EL_DC_TIMEGATE_SWITCH,
1406     0,
1407     InitTimegateWheel,
1408     RunTimegateWheel,
1409     NULL
1410   },
1411   {
1412     EL_EMC_MAGIC_BALL_ACTIVE,
1413     EL_EMC_MAGIC_BALL_ACTIVE,
1414     0,
1415     InitMagicBallDelay,
1416     NULL,
1417     ActivateMagicBall
1418   },
1419   {
1420     EL_EMC_SPRING_BUMPER_ACTIVE,
1421     EL_EMC_SPRING_BUMPER,
1422     8,
1423     NULL,
1424     NULL,
1425     NULL
1426   },
1427   {
1428     EL_DIAGONAL_SHRINKING,
1429     EL_UNDEFINED,
1430     0,
1431     NULL,
1432     NULL,
1433     NULL
1434   },
1435   {
1436     EL_DIAGONAL_GROWING,
1437     EL_UNDEFINED,
1438     0,
1439     NULL,
1440     NULL,
1441     NULL,
1442   },
1443
1444   {
1445     EL_UNDEFINED,
1446     EL_UNDEFINED,
1447     -1,
1448     NULL,
1449     NULL,
1450     NULL
1451   }
1452 };
1453
1454 struct
1455 {
1456   int element;
1457   int push_delay_fixed, push_delay_random;
1458 }
1459 push_delay_list[] =
1460 {
1461   { EL_SPRING,                  0, 0 },
1462   { EL_BALLOON,                 0, 0 },
1463
1464   { EL_SOKOBAN_OBJECT,          2, 0 },
1465   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1466   { EL_SATELLITE,               2, 0 },
1467   { EL_SP_DISK_YELLOW,          2, 0 },
1468
1469   { EL_UNDEFINED,               0, 0 },
1470 };
1471
1472 struct
1473 {
1474   int element;
1475   int move_stepsize;
1476 }
1477 move_stepsize_list[] =
1478 {
1479   { EL_AMOEBA_DROP,             2 },
1480   { EL_AMOEBA_DROPPING,         2 },
1481   { EL_QUICKSAND_FILLING,       1 },
1482   { EL_QUICKSAND_EMPTYING,      1 },
1483   { EL_QUICKSAND_FAST_FILLING,  2 },
1484   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1485   { EL_MAGIC_WALL_FILLING,      2 },
1486   { EL_MAGIC_WALL_EMPTYING,     2 },
1487   { EL_BD_MAGIC_WALL_FILLING,   2 },
1488   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1489   { EL_DC_MAGIC_WALL_FILLING,   2 },
1490   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1491
1492   { EL_UNDEFINED,               0 },
1493 };
1494
1495 struct
1496 {
1497   int element;
1498   int count;
1499 }
1500 collect_count_list[] =
1501 {
1502   { EL_EMERALD,                 1 },
1503   { EL_BD_DIAMOND,              1 },
1504   { EL_EMERALD_YELLOW,          1 },
1505   { EL_EMERALD_RED,             1 },
1506   { EL_EMERALD_PURPLE,          1 },
1507   { EL_DIAMOND,                 3 },
1508   { EL_SP_INFOTRON,             1 },
1509   { EL_PEARL,                   5 },
1510   { EL_CRYSTAL,                 8 },
1511
1512   { EL_UNDEFINED,               0 },
1513 };
1514
1515 struct
1516 {
1517   int element;
1518   int direction;
1519 }
1520 access_direction_list[] =
1521 {
1522   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1523   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1524   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1525   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1526   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1527   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1528   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1529   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1530   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1531   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1532   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1533
1534   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1535   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1536   { EL_SP_PORT_UP,                                                   MV_DOWN },
1537   { EL_SP_PORT_DOWN,                                         MV_UP           },
1538   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1539   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1540   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1541   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1542   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1543   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1544   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1545   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1546   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1547   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1548   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1549   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1550   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1551   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1552   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1553
1554   { EL_UNDEFINED,                       MV_NONE                              }
1555 };
1556
1557 static struct XY xy_topdown[] =
1558 {
1559   {  0, -1 },
1560   { -1,  0 },
1561   { +1,  0 },
1562   {  0, +1 }
1563 };
1564
1565 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1566
1567 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1568 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1569 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1570                                  IS_JUST_CHANGING(x, y))
1571
1572 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1573
1574 // static variables for playfield scan mode (scanning forward or backward)
1575 static int playfield_scan_start_x = 0;
1576 static int playfield_scan_start_y = 0;
1577 static int playfield_scan_delta_x = 1;
1578 static int playfield_scan_delta_y = 1;
1579
1580 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1581                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1582                                      (y) += playfield_scan_delta_y)     \
1583                                 for ((x) = playfield_scan_start_x;      \
1584                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1585                                      (x) += playfield_scan_delta_x)
1586
1587 #ifdef DEBUG
1588 void DEBUG_SetMaximumDynamite(void)
1589 {
1590   int i;
1591
1592   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1593     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1594       local_player->inventory_element[local_player->inventory_size++] =
1595         EL_DYNAMITE;
1596 }
1597 #endif
1598
1599 static void InitPlayfieldScanModeVars(void)
1600 {
1601   if (game.use_reverse_scan_direction)
1602   {
1603     playfield_scan_start_x = lev_fieldx - 1;
1604     playfield_scan_start_y = lev_fieldy - 1;
1605
1606     playfield_scan_delta_x = -1;
1607     playfield_scan_delta_y = -1;
1608   }
1609   else
1610   {
1611     playfield_scan_start_x = 0;
1612     playfield_scan_start_y = 0;
1613
1614     playfield_scan_delta_x = 1;
1615     playfield_scan_delta_y = 1;
1616   }
1617 }
1618
1619 static void InitPlayfieldScanMode(int mode)
1620 {
1621   game.use_reverse_scan_direction =
1622     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1623
1624   InitPlayfieldScanModeVars();
1625 }
1626
1627 static int get_move_delay_from_stepsize(int move_stepsize)
1628 {
1629   move_stepsize =
1630     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1631
1632   // make sure that stepsize value is always a power of 2
1633   move_stepsize = (1 << log_2(move_stepsize));
1634
1635   return TILEX / move_stepsize;
1636 }
1637
1638 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1639                                boolean init_game)
1640 {
1641   int player_nr = player->index_nr;
1642   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1643   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1644
1645   // do no immediately change move delay -- the player might just be moving
1646   player->move_delay_value_next = move_delay;
1647
1648   // information if player can move must be set separately
1649   player->cannot_move = cannot_move;
1650
1651   if (init_game)
1652   {
1653     player->move_delay       = game.initial_move_delay[player_nr];
1654     player->move_delay_value = game.initial_move_delay_value[player_nr];
1655
1656     player->move_delay_value_next = -1;
1657
1658     player->move_delay_reset_counter = 0;
1659   }
1660 }
1661
1662 void GetPlayerConfig(void)
1663 {
1664   GameFrameDelay = setup.game_frame_delay;
1665
1666   if (!audio.sound_available)
1667     setup.sound_simple = FALSE;
1668
1669   if (!audio.loops_available)
1670     setup.sound_loops = FALSE;
1671
1672   if (!audio.music_available)
1673     setup.sound_music = FALSE;
1674
1675   if (!video.fullscreen_available)
1676     setup.fullscreen = FALSE;
1677
1678   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1679
1680   SetAudioMode(setup.sound);
1681 }
1682
1683 int GetElementFromGroupElement(int element)
1684 {
1685   if (IS_GROUP_ELEMENT(element))
1686   {
1687     struct ElementGroupInfo *group = element_info[element].group;
1688     int last_anim_random_frame = gfx.anim_random_frame;
1689     int element_pos;
1690
1691     if (group->choice_mode == ANIM_RANDOM)
1692       gfx.anim_random_frame = RND(group->num_elements_resolved);
1693
1694     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1695                                     group->choice_mode, 0,
1696                                     group->choice_pos);
1697
1698     if (group->choice_mode == ANIM_RANDOM)
1699       gfx.anim_random_frame = last_anim_random_frame;
1700
1701     group->choice_pos++;
1702
1703     element = group->element_resolved[element_pos];
1704   }
1705
1706   return element;
1707 }
1708
1709 static void IncrementSokobanFieldsNeeded(void)
1710 {
1711   if (level.sb_fields_needed)
1712     game.sokoban_fields_still_needed++;
1713 }
1714
1715 static void IncrementSokobanObjectsNeeded(void)
1716 {
1717   if (level.sb_objects_needed)
1718     game.sokoban_objects_still_needed++;
1719 }
1720
1721 static void DecrementSokobanFieldsNeeded(void)
1722 {
1723   if (game.sokoban_fields_still_needed > 0)
1724     game.sokoban_fields_still_needed--;
1725 }
1726
1727 static void DecrementSokobanObjectsNeeded(void)
1728 {
1729   if (game.sokoban_objects_still_needed > 0)
1730     game.sokoban_objects_still_needed--;
1731 }
1732
1733 static void InitPlayerField(int x, int y, int element, boolean init_game)
1734 {
1735   if (element == EL_SP_MURPHY)
1736   {
1737     if (init_game)
1738     {
1739       if (stored_player[0].present)
1740       {
1741         Tile[x][y] = EL_SP_MURPHY_CLONE;
1742
1743         return;
1744       }
1745       else
1746       {
1747         stored_player[0].initial_element = element;
1748         stored_player[0].use_murphy = TRUE;
1749
1750         if (!level.use_artwork_element[0])
1751           stored_player[0].artwork_element = EL_SP_MURPHY;
1752       }
1753
1754       Tile[x][y] = EL_PLAYER_1;
1755     }
1756   }
1757
1758   if (init_game)
1759   {
1760     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1761     int jx = player->jx, jy = player->jy;
1762
1763     player->present = TRUE;
1764
1765     player->block_last_field = (element == EL_SP_MURPHY ?
1766                                 level.sp_block_last_field :
1767                                 level.block_last_field);
1768
1769     // ---------- initialize player's last field block delay ------------------
1770
1771     // always start with reliable default value (no adjustment needed)
1772     player->block_delay_adjustment = 0;
1773
1774     // special case 1: in Supaplex, Murphy blocks last field one more frame
1775     if (player->block_last_field && element == EL_SP_MURPHY)
1776       player->block_delay_adjustment = 1;
1777
1778     // special case 2: in game engines before 3.1.1, blocking was different
1779     if (game.use_block_last_field_bug)
1780       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1781
1782     if (!network.enabled || player->connected_network)
1783     {
1784       player->active = TRUE;
1785
1786       // remove potentially duplicate players
1787       if (IN_LEV_FIELD(jx, jy) && StorePlayer[jx][jy] == Tile[x][y])
1788         StorePlayer[jx][jy] = 0;
1789
1790       StorePlayer[x][y] = Tile[x][y];
1791
1792 #if DEBUG_INIT_PLAYER
1793       Debug("game:init:player", "- player element %d activated",
1794             player->element_nr);
1795       Debug("game:init:player", "  (local player is %d and currently %s)",
1796             local_player->element_nr,
1797             local_player->active ? "active" : "not active");
1798     }
1799 #endif
1800
1801     Tile[x][y] = EL_EMPTY;
1802
1803     player->jx = player->last_jx = x;
1804     player->jy = player->last_jy = y;
1805   }
1806
1807   // always check if player was just killed and should be reanimated
1808   {
1809     int player_nr = GET_PLAYER_NR(element);
1810     struct PlayerInfo *player = &stored_player[player_nr];
1811
1812     if (player->active && player->killed)
1813       player->reanimated = TRUE; // if player was just killed, reanimate him
1814   }
1815 }
1816
1817 static void InitField(int x, int y, boolean init_game)
1818 {
1819   int element = Tile[x][y];
1820
1821   switch (element)
1822   {
1823     case EL_SP_MURPHY:
1824     case EL_PLAYER_1:
1825     case EL_PLAYER_2:
1826     case EL_PLAYER_3:
1827     case EL_PLAYER_4:
1828       InitPlayerField(x, y, element, init_game);
1829       break;
1830
1831     case EL_SOKOBAN_FIELD_PLAYER:
1832       element = Tile[x][y] = EL_PLAYER_1;
1833       InitField(x, y, init_game);
1834
1835       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1836       InitField(x, y, init_game);
1837       break;
1838
1839     case EL_SOKOBAN_FIELD_EMPTY:
1840       IncrementSokobanFieldsNeeded();
1841       break;
1842
1843     case EL_SOKOBAN_OBJECT:
1844       IncrementSokobanObjectsNeeded();
1845       break;
1846
1847     case EL_STONEBLOCK:
1848       if (x < lev_fieldx - 1 && Tile[x + 1][y] == EL_ACID)
1849         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1850       else if (x > 0 && Tile[x - 1][y] == EL_ACID)
1851         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1852       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPLEFT)
1853         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1854       else if (y > 0 && Tile[x][y - 1] == EL_ACID)
1855         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1856       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPRIGHT)
1857         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1858       break;
1859
1860     case EL_BUG:
1861     case EL_BUG_RIGHT:
1862     case EL_BUG_UP:
1863     case EL_BUG_LEFT:
1864     case EL_BUG_DOWN:
1865     case EL_SPACESHIP:
1866     case EL_SPACESHIP_RIGHT:
1867     case EL_SPACESHIP_UP:
1868     case EL_SPACESHIP_LEFT:
1869     case EL_SPACESHIP_DOWN:
1870     case EL_BD_BUTTERFLY:
1871     case EL_BD_BUTTERFLY_RIGHT:
1872     case EL_BD_BUTTERFLY_UP:
1873     case EL_BD_BUTTERFLY_LEFT:
1874     case EL_BD_BUTTERFLY_DOWN:
1875     case EL_BD_FIREFLY:
1876     case EL_BD_FIREFLY_RIGHT:
1877     case EL_BD_FIREFLY_UP:
1878     case EL_BD_FIREFLY_LEFT:
1879     case EL_BD_FIREFLY_DOWN:
1880     case EL_PACMAN_RIGHT:
1881     case EL_PACMAN_UP:
1882     case EL_PACMAN_LEFT:
1883     case EL_PACMAN_DOWN:
1884     case EL_YAMYAM:
1885     case EL_YAMYAM_LEFT:
1886     case EL_YAMYAM_RIGHT:
1887     case EL_YAMYAM_UP:
1888     case EL_YAMYAM_DOWN:
1889     case EL_DARK_YAMYAM:
1890     case EL_ROBOT:
1891     case EL_PACMAN:
1892     case EL_SP_SNIKSNAK:
1893     case EL_SP_ELECTRON:
1894     case EL_MOLE:
1895     case EL_MOLE_LEFT:
1896     case EL_MOLE_RIGHT:
1897     case EL_MOLE_UP:
1898     case EL_MOLE_DOWN:
1899     case EL_SPRING_LEFT:
1900     case EL_SPRING_RIGHT:
1901       InitMovDir(x, y);
1902       break;
1903
1904     case EL_AMOEBA_FULL:
1905     case EL_BD_AMOEBA:
1906       InitAmoebaNr(x, y);
1907       break;
1908
1909     case EL_AMOEBA_DROP:
1910       if (y == lev_fieldy - 1)
1911       {
1912         Tile[x][y] = EL_AMOEBA_GROWING;
1913         Store[x][y] = EL_AMOEBA_WET;
1914       }
1915       break;
1916
1917     case EL_DYNAMITE_ACTIVE:
1918     case EL_SP_DISK_RED_ACTIVE:
1919     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1920     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1921     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1922     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1923       MovDelay[x][y] = 96;
1924       break;
1925
1926     case EL_EM_DYNAMITE_ACTIVE:
1927       MovDelay[x][y] = 32;
1928       break;
1929
1930     case EL_LAMP:
1931       game.lights_still_needed++;
1932       break;
1933
1934     case EL_PENGUIN:
1935       game.friends_still_needed++;
1936       break;
1937
1938     case EL_PIG:
1939     case EL_DRAGON:
1940       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1941       break;
1942
1943     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1944     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1945     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1946     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1947     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1948     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1949     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1950     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1951     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1952     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1953     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1954     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1955       if (init_game)
1956       {
1957         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1958         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1959         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1960
1961         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1962         {
1963           game.belt_dir[belt_nr] = belt_dir;
1964           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1965         }
1966         else    // more than one switch -- set it like the first switch
1967         {
1968           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1969         }
1970       }
1971       break;
1972
1973     case EL_LIGHT_SWITCH_ACTIVE:
1974       if (init_game)
1975         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1976       break;
1977
1978     case EL_INVISIBLE_STEELWALL:
1979     case EL_INVISIBLE_WALL:
1980     case EL_INVISIBLE_SAND:
1981       if (game.light_time_left > 0 ||
1982           game.lenses_time_left > 0)
1983         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1984       break;
1985
1986     case EL_EMC_MAGIC_BALL:
1987       if (game.ball_active)
1988         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1989       break;
1990
1991     case EL_EMC_MAGIC_BALL_SWITCH:
1992       if (game.ball_active)
1993         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1994       break;
1995
1996     case EL_TRIGGER_PLAYER:
1997     case EL_TRIGGER_ELEMENT:
1998     case EL_TRIGGER_CE_VALUE:
1999     case EL_TRIGGER_CE_SCORE:
2000     case EL_SELF:
2001     case EL_ANY_ELEMENT:
2002     case EL_CURRENT_CE_VALUE:
2003     case EL_CURRENT_CE_SCORE:
2004     case EL_PREV_CE_1:
2005     case EL_PREV_CE_2:
2006     case EL_PREV_CE_3:
2007     case EL_PREV_CE_4:
2008     case EL_PREV_CE_5:
2009     case EL_PREV_CE_6:
2010     case EL_PREV_CE_7:
2011     case EL_PREV_CE_8:
2012     case EL_NEXT_CE_1:
2013     case EL_NEXT_CE_2:
2014     case EL_NEXT_CE_3:
2015     case EL_NEXT_CE_4:
2016     case EL_NEXT_CE_5:
2017     case EL_NEXT_CE_6:
2018     case EL_NEXT_CE_7:
2019     case EL_NEXT_CE_8:
2020       // reference elements should not be used on the playfield
2021       Tile[x][y] = EL_EMPTY;
2022       break;
2023
2024     default:
2025       if (IS_CUSTOM_ELEMENT(element))
2026       {
2027         if (CAN_MOVE(element))
2028           InitMovDir(x, y);
2029
2030         if (!element_info[element].use_last_ce_value || init_game)
2031           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2032       }
2033       else if (IS_GROUP_ELEMENT(element))
2034       {
2035         Tile[x][y] = GetElementFromGroupElement(element);
2036
2037         InitField(x, y, init_game);
2038       }
2039       else if (IS_EMPTY_ELEMENT(element))
2040       {
2041         GfxElementEmpty[x][y] = element;
2042         Tile[x][y] = EL_EMPTY;
2043
2044         if (element_info[element].use_gfx_element)
2045           game.use_masked_elements = TRUE;
2046       }
2047
2048       break;
2049   }
2050
2051   if (!init_game)
2052     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2053 }
2054
2055 static void InitField_WithBug1(int x, int y, boolean init_game)
2056 {
2057   InitField(x, y, init_game);
2058
2059   // not needed to call InitMovDir() -- already done by InitField()!
2060   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2061       CAN_MOVE(Tile[x][y]))
2062     InitMovDir(x, y);
2063 }
2064
2065 static void InitField_WithBug2(int x, int y, boolean init_game)
2066 {
2067   int old_element = Tile[x][y];
2068
2069   InitField(x, y, init_game);
2070
2071   // not needed to call InitMovDir() -- already done by InitField()!
2072   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2073       CAN_MOVE(old_element) &&
2074       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2075     InitMovDir(x, y);
2076
2077   /* this case is in fact a combination of not less than three bugs:
2078      first, it calls InitMovDir() for elements that can move, although this is
2079      already done by InitField(); then, it checks the element that was at this
2080      field _before_ the call to InitField() (which can change it); lastly, it
2081      was not called for "mole with direction" elements, which were treated as
2082      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2083   */
2084 }
2085
2086 static int get_key_element_from_nr(int key_nr)
2087 {
2088   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2089                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2090                           EL_EM_KEY_1 : EL_KEY_1);
2091
2092   return key_base_element + key_nr;
2093 }
2094
2095 static int get_next_dropped_element(struct PlayerInfo *player)
2096 {
2097   return (player->inventory_size > 0 ?
2098           player->inventory_element[player->inventory_size - 1] :
2099           player->inventory_infinite_element != EL_UNDEFINED ?
2100           player->inventory_infinite_element :
2101           player->dynabombs_left > 0 ?
2102           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2103           EL_UNDEFINED);
2104 }
2105
2106 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2107 {
2108   // pos >= 0: get element from bottom of the stack;
2109   // pos <  0: get element from top of the stack
2110
2111   if (pos < 0)
2112   {
2113     int min_inventory_size = -pos;
2114     int inventory_pos = player->inventory_size - min_inventory_size;
2115     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2116
2117     return (player->inventory_size >= min_inventory_size ?
2118             player->inventory_element[inventory_pos] :
2119             player->inventory_infinite_element != EL_UNDEFINED ?
2120             player->inventory_infinite_element :
2121             player->dynabombs_left >= min_dynabombs_left ?
2122             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2123             EL_UNDEFINED);
2124   }
2125   else
2126   {
2127     int min_dynabombs_left = pos + 1;
2128     int min_inventory_size = pos + 1 - player->dynabombs_left;
2129     int inventory_pos = pos - player->dynabombs_left;
2130
2131     return (player->inventory_infinite_element != EL_UNDEFINED ?
2132             player->inventory_infinite_element :
2133             player->dynabombs_left >= min_dynabombs_left ?
2134             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2135             player->inventory_size >= min_inventory_size ?
2136             player->inventory_element[inventory_pos] :
2137             EL_UNDEFINED);
2138   }
2139 }
2140
2141 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2142 {
2143   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2144   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2145   int compare_result;
2146
2147   if (gpo1->sort_priority != gpo2->sort_priority)
2148     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2149   else
2150     compare_result = gpo1->nr - gpo2->nr;
2151
2152   return compare_result;
2153 }
2154
2155 int getPlayerInventorySize(int player_nr)
2156 {
2157   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2158     return game_em.ply[player_nr]->dynamite;
2159   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2160     return game_sp.red_disk_count;
2161   else
2162     return stored_player[player_nr].inventory_size;
2163 }
2164
2165 static void InitGameControlValues(void)
2166 {
2167   int i;
2168
2169   for (i = 0; game_panel_controls[i].nr != -1; i++)
2170   {
2171     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2172     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2173     struct TextPosInfo *pos = gpc->pos;
2174     int nr = gpc->nr;
2175     int type = gpc->type;
2176
2177     if (nr != i)
2178     {
2179       Error("'game_panel_controls' structure corrupted at %d", i);
2180
2181       Fail("this should not happen -- please debug");
2182     }
2183
2184     // force update of game controls after initialization
2185     gpc->value = gpc->last_value = -1;
2186     gpc->frame = gpc->last_frame = -1;
2187     gpc->gfx_frame = -1;
2188
2189     // determine panel value width for later calculation of alignment
2190     if (type == TYPE_INTEGER || type == TYPE_STRING)
2191     {
2192       pos->width = pos->size * getFontWidth(pos->font);
2193       pos->height = getFontHeight(pos->font);
2194     }
2195     else if (type == TYPE_ELEMENT)
2196     {
2197       pos->width = pos->size;
2198       pos->height = pos->size;
2199     }
2200
2201     // fill structure for game panel draw order
2202     gpo->nr = gpc->nr;
2203     gpo->sort_priority = pos->sort_priority;
2204   }
2205
2206   // sort game panel controls according to sort_priority and control number
2207   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2208         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2209 }
2210
2211 static void UpdatePlayfieldElementCount(void)
2212 {
2213   boolean use_element_count = FALSE;
2214   int i, j, x, y;
2215
2216   // first check if it is needed at all to calculate playfield element count
2217   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2218     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2219       use_element_count = TRUE;
2220
2221   if (!use_element_count)
2222     return;
2223
2224   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2225     element_info[i].element_count = 0;
2226
2227   SCAN_PLAYFIELD(x, y)
2228   {
2229     element_info[Tile[x][y]].element_count++;
2230   }
2231
2232   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2233     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2234       if (IS_IN_GROUP(j, i))
2235         element_info[EL_GROUP_START + i].element_count +=
2236           element_info[j].element_count;
2237 }
2238
2239 static void UpdateGameControlValues(void)
2240 {
2241   int i, k;
2242   int time = (game.LevelSolved ?
2243               game.LevelSolved_CountingTime :
2244               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2245               game_em.lev->time :
2246               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2247               game_sp.time_played :
2248               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2249               game_mm.energy_left :
2250               game.no_level_time_limit ? TimePlayed : TimeLeft);
2251   int score = (game.LevelSolved ?
2252                game.LevelSolved_CountingScore :
2253                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2254                game_em.lev->score :
2255                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2256                game_sp.score :
2257                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2258                game_mm.score :
2259                game.score);
2260   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2261               game_em.lev->gems_needed :
2262               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2263               game_sp.infotrons_still_needed :
2264               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2265               game_mm.kettles_still_needed :
2266               game.gems_still_needed);
2267   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2268                      game_em.lev->gems_needed > 0 :
2269                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2270                      game_sp.infotrons_still_needed > 0 :
2271                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2272                      game_mm.kettles_still_needed > 0 ||
2273                      game_mm.lights_still_needed > 0 :
2274                      game.gems_still_needed > 0 ||
2275                      game.sokoban_fields_still_needed > 0 ||
2276                      game.sokoban_objects_still_needed > 0 ||
2277                      game.lights_still_needed > 0);
2278   int health = (game.LevelSolved ?
2279                 game.LevelSolved_CountingHealth :
2280                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2281                 MM_HEALTH(game_mm.laser_overload_value) :
2282                 game.health);
2283   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2284
2285   UpdatePlayfieldElementCount();
2286
2287   // update game panel control values
2288
2289   // used instead of "level_nr" (for network games)
2290   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2291   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2292
2293   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2294   for (i = 0; i < MAX_NUM_KEYS; i++)
2295     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2296   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2297   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2298
2299   if (game.centered_player_nr == -1)
2300   {
2301     for (i = 0; i < MAX_PLAYERS; i++)
2302     {
2303       // only one player in Supaplex game engine
2304       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2305         break;
2306
2307       for (k = 0; k < MAX_NUM_KEYS; k++)
2308       {
2309         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2310         {
2311           if (game_em.ply[i]->keys & (1 << k))
2312             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2313               get_key_element_from_nr(k);
2314         }
2315         else if (stored_player[i].key[k])
2316           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2317             get_key_element_from_nr(k);
2318       }
2319
2320       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2321         getPlayerInventorySize(i);
2322
2323       if (stored_player[i].num_white_keys > 0)
2324         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2325           EL_DC_KEY_WHITE;
2326
2327       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2328         stored_player[i].num_white_keys;
2329     }
2330   }
2331   else
2332   {
2333     int player_nr = game.centered_player_nr;
2334
2335     for (k = 0; k < MAX_NUM_KEYS; k++)
2336     {
2337       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2338       {
2339         if (game_em.ply[player_nr]->keys & (1 << k))
2340           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2341             get_key_element_from_nr(k);
2342       }
2343       else if (stored_player[player_nr].key[k])
2344         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2345           get_key_element_from_nr(k);
2346     }
2347
2348     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2349       getPlayerInventorySize(player_nr);
2350
2351     if (stored_player[player_nr].num_white_keys > 0)
2352       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2353
2354     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2355       stored_player[player_nr].num_white_keys;
2356   }
2357
2358   // re-arrange keys on game panel, if needed or if defined by style settings
2359   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2360   {
2361     int nr = GAME_PANEL_KEY_1 + i;
2362     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2363     struct TextPosInfo *pos = gpc->pos;
2364
2365     // skip check if key is not in the player's inventory
2366     if (gpc->value == EL_EMPTY)
2367       continue;
2368
2369     // check if keys should be arranged on panel from left to right
2370     if (pos->style == STYLE_LEFTMOST_POSITION)
2371     {
2372       // check previous key positions (left from current key)
2373       for (k = 0; k < i; k++)
2374       {
2375         int nr_new = GAME_PANEL_KEY_1 + k;
2376
2377         if (game_panel_controls[nr_new].value == EL_EMPTY)
2378         {
2379           game_panel_controls[nr_new].value = gpc->value;
2380           gpc->value = EL_EMPTY;
2381
2382           break;
2383         }
2384       }
2385     }
2386
2387     // check if "undefined" keys can be placed at some other position
2388     if (pos->x == -1 && pos->y == -1)
2389     {
2390       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2391
2392       // 1st try: display key at the same position as normal or EM keys
2393       if (game_panel_controls[nr_new].value == EL_EMPTY)
2394       {
2395         game_panel_controls[nr_new].value = gpc->value;
2396       }
2397       else
2398       {
2399         // 2nd try: display key at the next free position in the key panel
2400         for (k = 0; k < STD_NUM_KEYS; k++)
2401         {
2402           nr_new = GAME_PANEL_KEY_1 + k;
2403
2404           if (game_panel_controls[nr_new].value == EL_EMPTY)
2405           {
2406             game_panel_controls[nr_new].value = gpc->value;
2407
2408             break;
2409           }
2410         }
2411       }
2412     }
2413   }
2414
2415   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2416   {
2417     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2418       get_inventory_element_from_pos(local_player, i);
2419     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2420       get_inventory_element_from_pos(local_player, -i - 1);
2421   }
2422
2423   game_panel_controls[GAME_PANEL_SCORE].value = score;
2424   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2425
2426   game_panel_controls[GAME_PANEL_TIME].value = time;
2427
2428   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2429   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2430   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2431
2432   if (level.time == 0)
2433     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2434   else
2435     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2436
2437   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2438   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2439
2440   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2441
2442   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2443     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2444      EL_EMPTY);
2445   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2446     local_player->shield_normal_time_left;
2447   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2448     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2449      EL_EMPTY);
2450   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2451     local_player->shield_deadly_time_left;
2452
2453   game_panel_controls[GAME_PANEL_EXIT].value =
2454     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2455
2456   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2457     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2458   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2459     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2460      EL_EMC_MAGIC_BALL_SWITCH);
2461
2462   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2463     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2464   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2465     game.light_time_left;
2466
2467   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2468     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2469   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2470     game.timegate_time_left;
2471
2472   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2473     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2474
2475   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2476     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2477   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2478     game.lenses_time_left;
2479
2480   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2481     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2482   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2483     game.magnify_time_left;
2484
2485   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2486     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2487      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2488      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2489      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2490      EL_BALLOON_SWITCH_NONE);
2491
2492   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2493     local_player->dynabomb_count;
2494   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2495     local_player->dynabomb_size;
2496   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2497     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2498
2499   game_panel_controls[GAME_PANEL_PENGUINS].value =
2500     game.friends_still_needed;
2501
2502   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2503     game.sokoban_objects_still_needed;
2504   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2505     game.sokoban_fields_still_needed;
2506
2507   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2508     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2509
2510   for (i = 0; i < NUM_BELTS; i++)
2511   {
2512     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2513       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2514        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2515     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2516       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2517   }
2518
2519   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2520     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2521   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2522     game.magic_wall_time_left;
2523
2524   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2525     local_player->gravity;
2526
2527   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2528     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2529
2530   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2531     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2532       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2533        game.panel.element[i].id : EL_UNDEFINED);
2534
2535   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2536     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2537       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2538        element_info[game.panel.element_count[i].id].element_count : 0);
2539
2540   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2541     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2542       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2543        element_info[game.panel.ce_score[i].id].collect_score : 0);
2544
2545   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2546     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2547       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2548        element_info[game.panel.ce_score_element[i].id].collect_score :
2549        EL_UNDEFINED);
2550
2551   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2552   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2553   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2554
2555   // update game panel control frames
2556
2557   for (i = 0; game_panel_controls[i].nr != -1; i++)
2558   {
2559     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2560
2561     if (gpc->type == TYPE_ELEMENT)
2562     {
2563       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2564       {
2565         int last_anim_random_frame = gfx.anim_random_frame;
2566         int element = gpc->value;
2567         int graphic = el2panelimg(element);
2568         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2569                                sync_random_frame :
2570                                graphic_info[graphic].anim_global_anim_sync ?
2571                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2572
2573         if (gpc->value != gpc->last_value)
2574         {
2575           gpc->gfx_frame = 0;
2576           gpc->gfx_random = init_gfx_random;
2577         }
2578         else
2579         {
2580           gpc->gfx_frame++;
2581
2582           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2583               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2584             gpc->gfx_random = init_gfx_random;
2585         }
2586
2587         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2588           gfx.anim_random_frame = gpc->gfx_random;
2589
2590         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2591           gpc->gfx_frame = element_info[element].collect_score;
2592
2593         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2594
2595         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2596           gfx.anim_random_frame = last_anim_random_frame;
2597       }
2598     }
2599     else if (gpc->type == TYPE_GRAPHIC)
2600     {
2601       if (gpc->graphic != IMG_UNDEFINED)
2602       {
2603         int last_anim_random_frame = gfx.anim_random_frame;
2604         int graphic = gpc->graphic;
2605         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2606                                sync_random_frame :
2607                                graphic_info[graphic].anim_global_anim_sync ?
2608                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2609
2610         if (gpc->value != gpc->last_value)
2611         {
2612           gpc->gfx_frame = 0;
2613           gpc->gfx_random = init_gfx_random;
2614         }
2615         else
2616         {
2617           gpc->gfx_frame++;
2618
2619           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2620               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2621             gpc->gfx_random = init_gfx_random;
2622         }
2623
2624         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2625           gfx.anim_random_frame = gpc->gfx_random;
2626
2627         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2628
2629         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2630           gfx.anim_random_frame = last_anim_random_frame;
2631       }
2632     }
2633   }
2634 }
2635
2636 static void DisplayGameControlValues(void)
2637 {
2638   boolean redraw_panel = FALSE;
2639   int i;
2640
2641   for (i = 0; game_panel_controls[i].nr != -1; i++)
2642   {
2643     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2644
2645     if (PANEL_DEACTIVATED(gpc->pos))
2646       continue;
2647
2648     if (gpc->value == gpc->last_value &&
2649         gpc->frame == gpc->last_frame)
2650       continue;
2651
2652     redraw_panel = TRUE;
2653   }
2654
2655   if (!redraw_panel)
2656     return;
2657
2658   // copy default game door content to main double buffer
2659
2660   // !!! CHECK AGAIN !!!
2661   SetPanelBackground();
2662   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2663   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2664
2665   // redraw game control buttons
2666   RedrawGameButtons();
2667
2668   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2669
2670   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2671   {
2672     int nr = game_panel_order[i].nr;
2673     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2674     struct TextPosInfo *pos = gpc->pos;
2675     int type = gpc->type;
2676     int value = gpc->value;
2677     int frame = gpc->frame;
2678     int size = pos->size;
2679     int font = pos->font;
2680     boolean draw_masked = pos->draw_masked;
2681     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2682
2683     if (PANEL_DEACTIVATED(pos))
2684       continue;
2685
2686     if (pos->class == get_hash_from_key("extra_panel_items") &&
2687         !setup.prefer_extra_panel_items)
2688       continue;
2689
2690     gpc->last_value = value;
2691     gpc->last_frame = frame;
2692
2693     if (type == TYPE_INTEGER)
2694     {
2695       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2696           nr == GAME_PANEL_INVENTORY_COUNT ||
2697           nr == GAME_PANEL_SCORE ||
2698           nr == GAME_PANEL_HIGHSCORE ||
2699           nr == GAME_PANEL_TIME)
2700       {
2701         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2702
2703         if (use_dynamic_size)           // use dynamic number of digits
2704         {
2705           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 :
2706                               nr == GAME_PANEL_INVENTORY_COUNT ||
2707                               nr == GAME_PANEL_TIME ? 1000 : 100000);
2708           int size_add = (nr == GAME_PANEL_LEVEL_NUMBER ||
2709                           nr == GAME_PANEL_INVENTORY_COUNT ||
2710                           nr == GAME_PANEL_TIME ? 1 : 2);
2711           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 :
2712                        nr == GAME_PANEL_INVENTORY_COUNT ||
2713                        nr == GAME_PANEL_TIME ? 3 : 5);
2714           int size2 = size1 + size_add;
2715           int font1 = pos->font;
2716           int font2 = pos->font_alt;
2717
2718           size = (value < value_change ? size1 : size2);
2719           font = (value < value_change ? font1 : font2);
2720         }
2721       }
2722
2723       // correct text size if "digits" is zero or less
2724       if (size <= 0)
2725         size = strlen(int2str(value, size));
2726
2727       // dynamically correct text alignment
2728       pos->width = size * getFontWidth(font);
2729
2730       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2731                   int2str(value, size), font, mask_mode);
2732     }
2733     else if (type == TYPE_ELEMENT)
2734     {
2735       int element, graphic;
2736       Bitmap *src_bitmap;
2737       int src_x, src_y;
2738       int width, height;
2739       int dst_x = PANEL_XPOS(pos);
2740       int dst_y = PANEL_YPOS(pos);
2741
2742       if (value != EL_UNDEFINED && value != EL_EMPTY)
2743       {
2744         element = value;
2745         graphic = el2panelimg(value);
2746
2747 #if 0
2748         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2749               element, EL_NAME(element), size);
2750 #endif
2751
2752         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2753           size = TILESIZE;
2754
2755         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2756                               &src_x, &src_y);
2757
2758         width  = graphic_info[graphic].width  * size / TILESIZE;
2759         height = graphic_info[graphic].height * size / TILESIZE;
2760
2761         if (draw_masked)
2762           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2763                            dst_x, dst_y);
2764         else
2765           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2766                      dst_x, dst_y);
2767       }
2768     }
2769     else if (type == TYPE_GRAPHIC)
2770     {
2771       int graphic        = gpc->graphic;
2772       int graphic_active = gpc->graphic_active;
2773       Bitmap *src_bitmap;
2774       int src_x, src_y;
2775       int width, height;
2776       int dst_x = PANEL_XPOS(pos);
2777       int dst_y = PANEL_YPOS(pos);
2778       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2779                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2780
2781       if (graphic != IMG_UNDEFINED && !skip)
2782       {
2783         if (pos->style == STYLE_REVERSE)
2784           value = 100 - value;
2785
2786         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2787
2788         if (pos->direction & MV_HORIZONTAL)
2789         {
2790           width  = graphic_info[graphic_active].width * value / 100;
2791           height = graphic_info[graphic_active].height;
2792
2793           if (pos->direction == MV_LEFT)
2794           {
2795             src_x += graphic_info[graphic_active].width - width;
2796             dst_x += graphic_info[graphic_active].width - width;
2797           }
2798         }
2799         else
2800         {
2801           width  = graphic_info[graphic_active].width;
2802           height = graphic_info[graphic_active].height * value / 100;
2803
2804           if (pos->direction == MV_UP)
2805           {
2806             src_y += graphic_info[graphic_active].height - height;
2807             dst_y += graphic_info[graphic_active].height - height;
2808           }
2809         }
2810
2811         if (draw_masked)
2812           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2813                            dst_x, dst_y);
2814         else
2815           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2816                      dst_x, dst_y);
2817
2818         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2819
2820         if (pos->direction & MV_HORIZONTAL)
2821         {
2822           if (pos->direction == MV_RIGHT)
2823           {
2824             src_x += width;
2825             dst_x += width;
2826           }
2827           else
2828           {
2829             dst_x = PANEL_XPOS(pos);
2830           }
2831
2832           width = graphic_info[graphic].width - width;
2833         }
2834         else
2835         {
2836           if (pos->direction == MV_DOWN)
2837           {
2838             src_y += height;
2839             dst_y += height;
2840           }
2841           else
2842           {
2843             dst_y = PANEL_YPOS(pos);
2844           }
2845
2846           height = graphic_info[graphic].height - height;
2847         }
2848
2849         if (draw_masked)
2850           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2851                            dst_x, dst_y);
2852         else
2853           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2854                      dst_x, dst_y);
2855       }
2856     }
2857     else if (type == TYPE_STRING)
2858     {
2859       boolean active = (value != 0);
2860       char *state_normal = "off";
2861       char *state_active = "on";
2862       char *state = (active ? state_active : state_normal);
2863       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2864                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2865                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2866                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2867
2868       if (nr == GAME_PANEL_GRAVITY_STATE)
2869       {
2870         int font1 = pos->font;          // (used for normal state)
2871         int font2 = pos->font_alt;      // (used for active state)
2872
2873         font = (active ? font2 : font1);
2874       }
2875
2876       if (s != NULL)
2877       {
2878         char *s_cut;
2879
2880         if (size <= 0)
2881         {
2882           // don't truncate output if "chars" is zero or less
2883           size = strlen(s);
2884
2885           // dynamically correct text alignment
2886           pos->width = size * getFontWidth(font);
2887         }
2888
2889         s_cut = getStringCopyN(s, size);
2890
2891         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2892                     s_cut, font, mask_mode);
2893
2894         free(s_cut);
2895       }
2896     }
2897
2898     redraw_mask |= REDRAW_DOOR_1;
2899   }
2900
2901   SetGameStatus(GAME_MODE_PLAYING);
2902 }
2903
2904 void UpdateAndDisplayGameControlValues(void)
2905 {
2906   if (tape.deactivate_display)
2907     return;
2908
2909   UpdateGameControlValues();
2910   DisplayGameControlValues();
2911 }
2912
2913 void UpdateGameDoorValues(void)
2914 {
2915   UpdateGameControlValues();
2916 }
2917
2918 void DrawGameDoorValues(void)
2919 {
2920   DisplayGameControlValues();
2921 }
2922
2923
2924 // ============================================================================
2925 // InitGameEngine()
2926 // ----------------------------------------------------------------------------
2927 // initialize game engine due to level / tape version number
2928 // ============================================================================
2929
2930 static void InitGameEngine(void)
2931 {
2932   int i, j, k, l, x, y;
2933
2934   // set game engine from tape file when re-playing, else from level file
2935   game.engine_version = (tape.playing ? tape.engine_version :
2936                          level.game_version);
2937
2938   // set single or multi-player game mode (needed for re-playing tapes)
2939   game.team_mode = setup.team_mode;
2940
2941   if (tape.playing)
2942   {
2943     int num_players = 0;
2944
2945     for (i = 0; i < MAX_PLAYERS; i++)
2946       if (tape.player_participates[i])
2947         num_players++;
2948
2949     // multi-player tapes contain input data for more than one player
2950     game.team_mode = (num_players > 1);
2951   }
2952
2953 #if 0
2954   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2955         level.game_version);
2956   Debug("game:init:level", "          tape.file_version   == %06d",
2957         tape.file_version);
2958   Debug("game:init:level", "          tape.game_version   == %06d",
2959         tape.game_version);
2960   Debug("game:init:level", "          tape.engine_version == %06d",
2961         tape.engine_version);
2962   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2963         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2964 #endif
2965
2966   // --------------------------------------------------------------------------
2967   // set flags for bugs and changes according to active game engine version
2968   // --------------------------------------------------------------------------
2969
2970   /*
2971     Summary of bugfix:
2972     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2973
2974     Bug was introduced in version:
2975     2.0.1
2976
2977     Bug was fixed in version:
2978     4.2.0.0
2979
2980     Description:
2981     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2982     but the property "can fall" was missing, which caused some levels to be
2983     unsolvable. This was fixed in version 4.2.0.0.
2984
2985     Affected levels/tapes:
2986     An example for a tape that was fixed by this bugfix is tape 029 from the
2987     level set "rnd_sam_bateman".
2988     The wrong behaviour will still be used for all levels or tapes that were
2989     created/recorded with it. An example for this is tape 023 from the level
2990     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2991   */
2992
2993   boolean use_amoeba_dropping_cannot_fall_bug =
2994     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2995       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2996      (tape.playing &&
2997       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2998       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2999
3000   /*
3001     Summary of bugfix/change:
3002     Fixed move speed of elements entering or leaving magic wall.
3003
3004     Fixed/changed in version:
3005     2.0.1
3006
3007     Description:
3008     Before 2.0.1, move speed of elements entering or leaving magic wall was
3009     twice as fast as it is now.
3010     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
3011
3012     Affected levels/tapes:
3013     The first condition is generally needed for all levels/tapes before version
3014     2.0.1, which might use the old behaviour before it was changed; known tapes
3015     that are affected: Tape 014 from the level set "rnd_conor_mancone".
3016     The second condition is an exception from the above case and is needed for
3017     the special case of tapes recorded with game (not engine!) version 2.0.1 or
3018     above, but before it was known that this change would break tapes like the
3019     above and was fixed in 4.2.0.0, so that the changed behaviour was active
3020     although the engine version while recording maybe was before 2.0.1. There
3021     are a lot of tapes that are affected by this exception, like tape 006 from
3022     the level set "rnd_conor_mancone".
3023   */
3024
3025   boolean use_old_move_stepsize_for_magic_wall =
3026     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
3027      !(tape.playing &&
3028        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3029        tape.game_version <  VERSION_IDENT(4,2,0,0)));
3030
3031   /*
3032     Summary of bugfix/change:
3033     Fixed handling for custom elements that change when pushed by the player.
3034
3035     Fixed/changed in version:
3036     3.1.0
3037
3038     Description:
3039     Before 3.1.0, custom elements that "change when pushing" changed directly
3040     after the player started pushing them (until then handled in "DigField()").
3041     Since 3.1.0, these custom elements are not changed until the "pushing"
3042     move of the element is finished (now handled in "ContinueMoving()").
3043
3044     Affected levels/tapes:
3045     The first condition is generally needed for all levels/tapes before version
3046     3.1.0, which might use the old behaviour before it was changed; known tapes
3047     that are affected are some tapes from the level set "Walpurgis Gardens" by
3048     Jamie Cullen.
3049     The second condition is an exception from the above case and is needed for
3050     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3051     above (including some development versions of 3.1.0), but before it was
3052     known that this change would break tapes like the above and was fixed in
3053     3.1.1, so that the changed behaviour was active although the engine version
3054     while recording maybe was before 3.1.0. There is at least one tape that is
3055     affected by this exception, which is the tape for the one-level set "Bug
3056     Machine" by Juergen Bonhagen.
3057   */
3058
3059   game.use_change_when_pushing_bug =
3060     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3061      !(tape.playing &&
3062        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3063        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3064
3065   /*
3066     Summary of bugfix/change:
3067     Fixed handling for blocking the field the player leaves when moving.
3068
3069     Fixed/changed in version:
3070     3.1.1
3071
3072     Description:
3073     Before 3.1.1, when "block last field when moving" was enabled, the field
3074     the player is leaving when moving was blocked for the time of the move,
3075     and was directly unblocked afterwards. This resulted in the last field
3076     being blocked for exactly one less than the number of frames of one player
3077     move. Additionally, even when blocking was disabled, the last field was
3078     blocked for exactly one frame.
3079     Since 3.1.1, due to changes in player movement handling, the last field
3080     is not blocked at all when blocking is disabled. When blocking is enabled,
3081     the last field is blocked for exactly the number of frames of one player
3082     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3083     last field is blocked for exactly one more than the number of frames of
3084     one player move.
3085
3086     Affected levels/tapes:
3087     (!!! yet to be determined -- probably many !!!)
3088   */
3089
3090   game.use_block_last_field_bug =
3091     (game.engine_version < VERSION_IDENT(3,1,1,0));
3092
3093   /* various special flags and settings for native Emerald Mine game engine */
3094
3095   game_em.use_single_button =
3096     (game.engine_version > VERSION_IDENT(4,0,0,2));
3097
3098   game_em.use_snap_key_bug =
3099     (game.engine_version < VERSION_IDENT(4,0,1,0));
3100
3101   game_em.use_random_bug =
3102     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3103
3104   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3105
3106   game_em.use_old_explosions            = use_old_em_engine;
3107   game_em.use_old_android               = use_old_em_engine;
3108   game_em.use_old_push_elements         = use_old_em_engine;
3109   game_em.use_old_push_into_acid        = use_old_em_engine;
3110
3111   game_em.use_wrap_around               = !use_old_em_engine;
3112
3113   // --------------------------------------------------------------------------
3114
3115   // set maximal allowed number of custom element changes per game frame
3116   game.max_num_changes_per_frame = 1;
3117
3118   // default scan direction: scan playfield from top/left to bottom/right
3119   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3120
3121   // dynamically adjust element properties according to game engine version
3122   InitElementPropertiesEngine(game.engine_version);
3123
3124   // ---------- initialize special element properties -------------------------
3125
3126   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3127   if (use_amoeba_dropping_cannot_fall_bug)
3128     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3129
3130   // ---------- initialize player's initial move delay ------------------------
3131
3132   // dynamically adjust player properties according to level information
3133   for (i = 0; i < MAX_PLAYERS; i++)
3134     game.initial_move_delay_value[i] =
3135       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3136
3137   // dynamically adjust player properties according to game engine version
3138   for (i = 0; i < MAX_PLAYERS; i++)
3139     game.initial_move_delay[i] =
3140       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3141        game.initial_move_delay_value[i] : 0);
3142
3143   // ---------- initialize player's initial push delay ------------------------
3144
3145   // dynamically adjust player properties according to game engine version
3146   game.initial_push_delay_value =
3147     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3148
3149   // ---------- initialize changing elements ----------------------------------
3150
3151   // initialize changing elements information
3152   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3153   {
3154     struct ElementInfo *ei = &element_info[i];
3155
3156     // this pointer might have been changed in the level editor
3157     ei->change = &ei->change_page[0];
3158
3159     if (!IS_CUSTOM_ELEMENT(i))
3160     {
3161       ei->change->target_element = EL_EMPTY_SPACE;
3162       ei->change->delay_fixed = 0;
3163       ei->change->delay_random = 0;
3164       ei->change->delay_frames = 1;
3165     }
3166
3167     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3168     {
3169       ei->has_change_event[j] = FALSE;
3170
3171       ei->event_page_nr[j] = 0;
3172       ei->event_page[j] = &ei->change_page[0];
3173     }
3174   }
3175
3176   // add changing elements from pre-defined list
3177   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3178   {
3179     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3180     struct ElementInfo *ei = &element_info[ch_delay->element];
3181
3182     ei->change->target_element       = ch_delay->target_element;
3183     ei->change->delay_fixed          = ch_delay->change_delay;
3184
3185     ei->change->pre_change_function  = ch_delay->pre_change_function;
3186     ei->change->change_function      = ch_delay->change_function;
3187     ei->change->post_change_function = ch_delay->post_change_function;
3188
3189     ei->change->can_change = TRUE;
3190     ei->change->can_change_or_has_action = TRUE;
3191
3192     ei->has_change_event[CE_DELAY] = TRUE;
3193
3194     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3195     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3196   }
3197
3198   // ---------- initialize if element can trigger global animations -----------
3199
3200   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3201   {
3202     struct ElementInfo *ei = &element_info[i];
3203
3204     ei->has_anim_event = FALSE;
3205   }
3206
3207   InitGlobalAnimEventsForCustomElements();
3208
3209   // ---------- initialize internal run-time variables ------------------------
3210
3211   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3212   {
3213     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3214
3215     for (j = 0; j < ei->num_change_pages; j++)
3216     {
3217       ei->change_page[j].can_change_or_has_action =
3218         (ei->change_page[j].can_change |
3219          ei->change_page[j].has_action);
3220     }
3221   }
3222
3223   // add change events from custom element configuration
3224   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3225   {
3226     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3227
3228     for (j = 0; j < ei->num_change_pages; j++)
3229     {
3230       if (!ei->change_page[j].can_change_or_has_action)
3231         continue;
3232
3233       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3234       {
3235         // only add event page for the first page found with this event
3236         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3237         {
3238           ei->has_change_event[k] = TRUE;
3239
3240           ei->event_page_nr[k] = j;
3241           ei->event_page[k] = &ei->change_page[j];
3242         }
3243       }
3244     }
3245   }
3246
3247   // ---------- initialize reference elements in change conditions ------------
3248
3249   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3250   {
3251     int element = EL_CUSTOM_START + i;
3252     struct ElementInfo *ei = &element_info[element];
3253
3254     for (j = 0; j < ei->num_change_pages; j++)
3255     {
3256       int trigger_element = ei->change_page[j].initial_trigger_element;
3257
3258       if (trigger_element >= EL_PREV_CE_8 &&
3259           trigger_element <= EL_NEXT_CE_8)
3260         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3261
3262       ei->change_page[j].trigger_element = trigger_element;
3263     }
3264   }
3265
3266   // ---------- initialize run-time trigger player and element ----------------
3267
3268   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3269   {
3270     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3271
3272     for (j = 0; j < ei->num_change_pages; j++)
3273     {
3274       struct ElementChangeInfo *change = &ei->change_page[j];
3275
3276       change->actual_trigger_element = EL_EMPTY;
3277       change->actual_trigger_player = EL_EMPTY;
3278       change->actual_trigger_player_bits = CH_PLAYER_NONE;
3279       change->actual_trigger_side = CH_SIDE_NONE;
3280       change->actual_trigger_ce_value = 0;
3281       change->actual_trigger_ce_score = 0;
3282     }
3283   }
3284
3285   // ---------- initialize trigger events -------------------------------------
3286
3287   // initialize trigger events information
3288   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3289     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3290       trigger_events[i][j] = FALSE;
3291
3292   // add trigger events from element change event properties
3293   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3294   {
3295     struct ElementInfo *ei = &element_info[i];
3296
3297     for (j = 0; j < ei->num_change_pages; j++)
3298     {
3299       struct ElementChangeInfo *change = &ei->change_page[j];
3300
3301       if (!change->can_change_or_has_action)
3302         continue;
3303
3304       if (change->has_event[CE_BY_OTHER_ACTION])
3305       {
3306         int trigger_element = change->trigger_element;
3307
3308         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3309         {
3310           if (change->has_event[k])
3311           {
3312             if (IS_GROUP_ELEMENT(trigger_element))
3313             {
3314               struct ElementGroupInfo *group =
3315                 element_info[trigger_element].group;
3316
3317               for (l = 0; l < group->num_elements_resolved; l++)
3318                 trigger_events[group->element_resolved[l]][k] = TRUE;
3319             }
3320             else if (trigger_element == EL_ANY_ELEMENT)
3321               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3322                 trigger_events[l][k] = TRUE;
3323             else
3324               trigger_events[trigger_element][k] = TRUE;
3325           }
3326         }
3327       }
3328     }
3329   }
3330
3331   // ---------- initialize push delay -----------------------------------------
3332
3333   // initialize push delay values to default
3334   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3335   {
3336     if (!IS_CUSTOM_ELEMENT(i))
3337     {
3338       // set default push delay values (corrected since version 3.0.7-1)
3339       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3340       {
3341         element_info[i].push_delay_fixed = 2;
3342         element_info[i].push_delay_random = 8;
3343       }
3344       else
3345       {
3346         element_info[i].push_delay_fixed = 8;
3347         element_info[i].push_delay_random = 8;
3348       }
3349     }
3350   }
3351
3352   // set push delay value for certain elements from pre-defined list
3353   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3354   {
3355     int e = push_delay_list[i].element;
3356
3357     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3358     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3359   }
3360
3361   // set push delay value for Supaplex elements for newer engine versions
3362   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3363   {
3364     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3365     {
3366       if (IS_SP_ELEMENT(i))
3367       {
3368         // set SP push delay to just enough to push under a falling zonk
3369         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3370
3371         element_info[i].push_delay_fixed  = delay;
3372         element_info[i].push_delay_random = 0;
3373       }
3374     }
3375   }
3376
3377   // ---------- initialize move stepsize --------------------------------------
3378
3379   // initialize move stepsize values to default
3380   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3381     if (!IS_CUSTOM_ELEMENT(i))
3382       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3383
3384   // set move stepsize value for certain elements from pre-defined list
3385   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3386   {
3387     int e = move_stepsize_list[i].element;
3388
3389     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3390
3391     // set move stepsize value for certain elements for older engine versions
3392     if (use_old_move_stepsize_for_magic_wall)
3393     {
3394       if (e == EL_MAGIC_WALL_FILLING ||
3395           e == EL_MAGIC_WALL_EMPTYING ||
3396           e == EL_BD_MAGIC_WALL_FILLING ||
3397           e == EL_BD_MAGIC_WALL_EMPTYING)
3398         element_info[e].move_stepsize *= 2;
3399     }
3400   }
3401
3402   // ---------- initialize collect score --------------------------------------
3403
3404   // initialize collect score values for custom elements from initial value
3405   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3406     if (IS_CUSTOM_ELEMENT(i))
3407       element_info[i].collect_score = element_info[i].collect_score_initial;
3408
3409   // ---------- initialize collect count --------------------------------------
3410
3411   // initialize collect count values for non-custom elements
3412   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3413     if (!IS_CUSTOM_ELEMENT(i))
3414       element_info[i].collect_count_initial = 0;
3415
3416   // add collect count values for all elements from pre-defined list
3417   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3418     element_info[collect_count_list[i].element].collect_count_initial =
3419       collect_count_list[i].count;
3420
3421   // ---------- initialize access direction -----------------------------------
3422
3423   // initialize access direction values to default (access from every side)
3424   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3425     if (!IS_CUSTOM_ELEMENT(i))
3426       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3427
3428   // set access direction value for certain elements from pre-defined list
3429   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3430     element_info[access_direction_list[i].element].access_direction =
3431       access_direction_list[i].direction;
3432
3433   // ---------- initialize explosion content ----------------------------------
3434   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3435   {
3436     if (IS_CUSTOM_ELEMENT(i))
3437       continue;
3438
3439     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3440     {
3441       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3442
3443       element_info[i].content.e[x][y] =
3444         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3445          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3446          i == EL_PLAYER_3 ? EL_EMERALD :
3447          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3448          i == EL_MOLE ? EL_EMERALD_RED :
3449          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3450          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3451          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3452          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3453          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3454          i == EL_WALL_EMERALD ? EL_EMERALD :
3455          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3456          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3457          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3458          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3459          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3460          i == EL_WALL_PEARL ? EL_PEARL :
3461          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3462          EL_EMPTY);
3463     }
3464   }
3465
3466   // ---------- initialize recursion detection --------------------------------
3467   recursion_loop_depth = 0;
3468   recursion_loop_detected = FALSE;
3469   recursion_loop_element = EL_UNDEFINED;
3470
3471   // ---------- initialize graphics engine ------------------------------------
3472   game.scroll_delay_value =
3473     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3474      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3475      !setup.forced_scroll_delay           ? 0 :
3476      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3477   game.scroll_delay_value =
3478     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3479
3480   // ---------- initialize game engine snapshots ------------------------------
3481   for (i = 0; i < MAX_PLAYERS; i++)
3482     game.snapshot.last_action[i] = 0;
3483   game.snapshot.changed_action = FALSE;
3484   game.snapshot.collected_item = FALSE;
3485   game.snapshot.mode =
3486     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3487      SNAPSHOT_MODE_EVERY_STEP :
3488      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3489      SNAPSHOT_MODE_EVERY_MOVE :
3490      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3491      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3492   game.snapshot.save_snapshot = FALSE;
3493
3494   // ---------- initialize level time for Supaplex engine ---------------------
3495   // Supaplex levels with time limit currently unsupported -- should be added
3496   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3497     level.time = 0;
3498
3499   // ---------- initialize flags for handling game actions --------------------
3500
3501   // set flags for game actions to default values
3502   game.use_key_actions = TRUE;
3503   game.use_mouse_actions = FALSE;
3504
3505   // when using Mirror Magic game engine, handle mouse events only
3506   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3507   {
3508     game.use_key_actions = FALSE;
3509     game.use_mouse_actions = TRUE;
3510   }
3511
3512   // check for custom elements with mouse click events
3513   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3514   {
3515     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3516     {
3517       int element = EL_CUSTOM_START + i;
3518
3519       if (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3520           HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3521           HAS_ANY_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3522           HAS_ANY_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3523         game.use_mouse_actions = TRUE;
3524     }
3525   }
3526 }
3527
3528 static int get_num_special_action(int element, int action_first,
3529                                   int action_last)
3530 {
3531   int num_special_action = 0;
3532   int i, j;
3533
3534   for (i = action_first; i <= action_last; i++)
3535   {
3536     boolean found = FALSE;
3537
3538     for (j = 0; j < NUM_DIRECTIONS; j++)
3539       if (el_act_dir2img(element, i, j) !=
3540           el_act_dir2img(element, ACTION_DEFAULT, j))
3541         found = TRUE;
3542
3543     if (found)
3544       num_special_action++;
3545     else
3546       break;
3547   }
3548
3549   return num_special_action;
3550 }
3551
3552
3553 // ============================================================================
3554 // InitGame()
3555 // ----------------------------------------------------------------------------
3556 // initialize and start new game
3557 // ============================================================================
3558
3559 #if DEBUG_INIT_PLAYER
3560 static void DebugPrintPlayerStatus(char *message)
3561 {
3562   int i;
3563
3564   if (!options.debug)
3565     return;
3566
3567   Debug("game:init:player", "%s:", message);
3568
3569   for (i = 0; i < MAX_PLAYERS; i++)
3570   {
3571     struct PlayerInfo *player = &stored_player[i];
3572
3573     Debug("game:init:player",
3574           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3575           i + 1,
3576           player->present,
3577           player->connected,
3578           player->connected_locally,
3579           player->connected_network,
3580           player->active,
3581           (local_player == player ? " (local player)" : ""));
3582   }
3583 }
3584 #endif
3585
3586 void InitGame(void)
3587 {
3588   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3589   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3590   int fade_mask = REDRAW_FIELD;
3591   boolean restarting = (game_status == GAME_MODE_PLAYING);
3592   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3593   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3594   int initial_move_dir = MV_DOWN;
3595   int i, j, x, y;
3596
3597   // required here to update video display before fading (FIX THIS)
3598   DrawMaskedBorder(REDRAW_DOOR_2);
3599
3600   if (!game.restart_level)
3601     CloseDoor(DOOR_CLOSE_1);
3602
3603   if (restarting)
3604   {
3605     // force fading out global animations displayed during game play
3606     SetGameStatus(GAME_MODE_PSEUDO_RESTARTING);
3607   }
3608   else
3609   {
3610     SetGameStatus(GAME_MODE_PLAYING);
3611   }
3612
3613   if (level_editor_test_game)
3614     FadeSkipNextFadeOut();
3615   else
3616     FadeSetEnterScreen();
3617
3618   if (CheckFadeAll())
3619     fade_mask = REDRAW_ALL;
3620
3621   FadeLevelSoundsAndMusic();
3622
3623   ExpireSoundLoops(TRUE);
3624
3625   FadeOut(fade_mask);
3626
3627   if (restarting)
3628   {
3629     // force restarting global animations displayed during game play
3630     RestartGlobalAnimsByStatus(GAME_MODE_PSEUDO_RESTARTING);
3631
3632     // this is required for "transforming" fade modes like cross-fading
3633     // (else global animations will be stopped, but not restarted here)
3634     SetAnimStatusBeforeFading(GAME_MODE_PSEUDO_RESTARTING);
3635
3636     SetGameStatus(GAME_MODE_PLAYING);
3637   }
3638
3639   if (level_editor_test_game)
3640     FadeSkipNextFadeIn();
3641
3642   // needed if different viewport properties defined for playing
3643   ChangeViewportPropertiesIfNeeded();
3644
3645   ClearField();
3646
3647   DrawCompleteVideoDisplay();
3648
3649   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3650
3651   InitGameEngine();
3652   InitGameControlValues();
3653
3654   if (tape.recording)
3655   {
3656     // initialize tape actions from game when recording tape
3657     tape.use_key_actions   = game.use_key_actions;
3658     tape.use_mouse_actions = game.use_mouse_actions;
3659
3660     // initialize visible playfield size when recording tape (for team mode)
3661     tape.scr_fieldx = SCR_FIELDX;
3662     tape.scr_fieldy = SCR_FIELDY;
3663   }
3664
3665   // don't play tapes over network
3666   network_playing = (network.enabled && !tape.playing);
3667
3668   for (i = 0; i < MAX_PLAYERS; i++)
3669   {
3670     struct PlayerInfo *player = &stored_player[i];
3671
3672     player->index_nr = i;
3673     player->index_bit = (1 << i);
3674     player->element_nr = EL_PLAYER_1 + i;
3675
3676     player->present = FALSE;
3677     player->active = FALSE;
3678     player->mapped = FALSE;
3679
3680     player->killed = FALSE;
3681     player->reanimated = FALSE;
3682     player->buried = FALSE;
3683
3684     player->action = 0;
3685     player->effective_action = 0;
3686     player->programmed_action = 0;
3687     player->snap_action = 0;
3688
3689     player->mouse_action.lx = 0;
3690     player->mouse_action.ly = 0;
3691     player->mouse_action.button = 0;
3692     player->mouse_action.button_hint = 0;
3693
3694     player->effective_mouse_action.lx = 0;
3695     player->effective_mouse_action.ly = 0;
3696     player->effective_mouse_action.button = 0;
3697     player->effective_mouse_action.button_hint = 0;
3698
3699     for (j = 0; j < MAX_NUM_KEYS; j++)
3700       player->key[j] = FALSE;
3701
3702     player->num_white_keys = 0;
3703
3704     player->dynabomb_count = 0;
3705     player->dynabomb_size = 1;
3706     player->dynabombs_left = 0;
3707     player->dynabomb_xl = FALSE;
3708
3709     player->MovDir = initial_move_dir;
3710     player->MovPos = 0;
3711     player->GfxPos = 0;
3712     player->GfxDir = initial_move_dir;
3713     player->GfxAction = ACTION_DEFAULT;
3714     player->Frame = 0;
3715     player->StepFrame = 0;
3716
3717     player->initial_element = player->element_nr;
3718     player->artwork_element =
3719       (level.use_artwork_element[i] ? level.artwork_element[i] :
3720        player->element_nr);
3721     player->use_murphy = FALSE;
3722
3723     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3724     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3725
3726     player->gravity = level.initial_player_gravity[i];
3727
3728     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3729
3730     player->actual_frame_counter.count = 0;
3731     player->actual_frame_counter.value = 1;
3732
3733     player->step_counter = 0;
3734
3735     player->last_move_dir = initial_move_dir;
3736
3737     player->is_active = FALSE;
3738
3739     player->is_waiting = FALSE;
3740     player->is_moving = FALSE;
3741     player->is_auto_moving = FALSE;
3742     player->is_digging = FALSE;
3743     player->is_snapping = FALSE;
3744     player->is_collecting = FALSE;
3745     player->is_pushing = FALSE;
3746     player->is_switching = FALSE;
3747     player->is_dropping = FALSE;
3748     player->is_dropping_pressed = FALSE;
3749
3750     player->is_bored = FALSE;
3751     player->is_sleeping = FALSE;
3752
3753     player->was_waiting = TRUE;
3754     player->was_moving = FALSE;
3755     player->was_snapping = FALSE;
3756     player->was_dropping = FALSE;
3757
3758     player->force_dropping = FALSE;
3759
3760     player->frame_counter_bored = -1;
3761     player->frame_counter_sleeping = -1;
3762
3763     player->anim_delay_counter = 0;
3764     player->post_delay_counter = 0;
3765
3766     player->dir_waiting = initial_move_dir;
3767     player->action_waiting = ACTION_DEFAULT;
3768     player->last_action_waiting = ACTION_DEFAULT;
3769     player->special_action_bored = ACTION_DEFAULT;
3770     player->special_action_sleeping = ACTION_DEFAULT;
3771
3772     player->switch_x = -1;
3773     player->switch_y = -1;
3774
3775     player->drop_x = -1;
3776     player->drop_y = -1;
3777
3778     player->show_envelope = 0;
3779
3780     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3781
3782     player->push_delay       = -1;      // initialized when pushing starts
3783     player->push_delay_value = game.initial_push_delay_value;
3784
3785     player->drop_delay = 0;
3786     player->drop_pressed_delay = 0;
3787
3788     player->last_jx = -1;
3789     player->last_jy = -1;
3790     player->jx = -1;
3791     player->jy = -1;
3792
3793     player->shield_normal_time_left = 0;
3794     player->shield_deadly_time_left = 0;
3795
3796     player->last_removed_element = EL_UNDEFINED;
3797
3798     player->inventory_infinite_element = EL_UNDEFINED;
3799     player->inventory_size = 0;
3800
3801     if (level.use_initial_inventory[i])
3802     {
3803       for (j = 0; j < level.initial_inventory_size[i]; j++)
3804       {
3805         int element = level.initial_inventory_content[i][j];
3806         int collect_count = element_info[element].collect_count_initial;
3807         int k;
3808
3809         if (!IS_CUSTOM_ELEMENT(element))
3810           collect_count = 1;
3811
3812         if (collect_count == 0)
3813           player->inventory_infinite_element = element;
3814         else
3815           for (k = 0; k < collect_count; k++)
3816             if (player->inventory_size < MAX_INVENTORY_SIZE)
3817               player->inventory_element[player->inventory_size++] = element;
3818       }
3819     }
3820
3821     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3822     SnapField(player, 0, 0);
3823
3824     map_player_action[i] = i;
3825   }
3826
3827   network_player_action_received = FALSE;
3828
3829   // initial null action
3830   if (network_playing)
3831     SendToServer_MovePlayer(MV_NONE);
3832
3833   FrameCounter = 0;
3834   TimeFrames = 0;
3835   TimePlayed = 0;
3836   TimeLeft = level.time;
3837   TapeTime = 0;
3838
3839   ScreenMovDir = MV_NONE;
3840   ScreenMovPos = 0;
3841   ScreenGfxPos = 0;
3842
3843   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3844
3845   game.robot_wheel_x = -1;
3846   game.robot_wheel_y = -1;
3847
3848   game.exit_x = -1;
3849   game.exit_y = -1;
3850
3851   game.all_players_gone = FALSE;
3852
3853   game.LevelSolved = FALSE;
3854   game.GameOver = FALSE;
3855
3856   game.GamePlayed = !tape.playing;
3857
3858   game.LevelSolved_GameWon = FALSE;
3859   game.LevelSolved_GameEnd = FALSE;
3860   game.LevelSolved_SaveTape = FALSE;
3861   game.LevelSolved_SaveScore = FALSE;
3862
3863   game.LevelSolved_CountingTime = 0;
3864   game.LevelSolved_CountingScore = 0;
3865   game.LevelSolved_CountingHealth = 0;
3866
3867   game.panel.active = TRUE;
3868
3869   game.no_level_time_limit = (level.time == 0);
3870   game.time_limit = (leveldir_current->time_limit && setup.time_limit);
3871
3872   game.yamyam_content_nr = 0;
3873   game.robot_wheel_active = FALSE;
3874   game.magic_wall_active = FALSE;
3875   game.magic_wall_time_left = 0;
3876   game.light_time_left = 0;
3877   game.timegate_time_left = 0;
3878   game.switchgate_pos = 0;
3879   game.wind_direction = level.wind_direction_initial;
3880
3881   game.time_final = 0;
3882   game.score_time_final = 0;
3883
3884   game.score = 0;
3885   game.score_final = 0;
3886
3887   game.health = MAX_HEALTH;
3888   game.health_final = MAX_HEALTH;
3889
3890   game.gems_still_needed = level.gems_needed;
3891   game.sokoban_fields_still_needed = 0;
3892   game.sokoban_objects_still_needed = 0;
3893   game.lights_still_needed = 0;
3894   game.players_still_needed = 0;
3895   game.friends_still_needed = 0;
3896
3897   game.lenses_time_left = 0;
3898   game.magnify_time_left = 0;
3899
3900   game.ball_active = level.ball_active_initial;
3901   game.ball_content_nr = 0;
3902
3903   game.explosions_delayed = TRUE;
3904
3905   game.envelope_active = FALSE;
3906
3907   // special case: set custom artwork setting to initial value
3908   game.use_masked_elements = game.use_masked_elements_initial;
3909
3910   for (i = 0; i < NUM_BELTS; i++)
3911   {
3912     game.belt_dir[i] = MV_NONE;
3913     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3914   }
3915
3916   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3917     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3918
3919 #if DEBUG_INIT_PLAYER
3920   DebugPrintPlayerStatus("Player status at level initialization");
3921 #endif
3922
3923   SCAN_PLAYFIELD(x, y)
3924   {
3925     Tile[x][y] = Last[x][y] = level.field[x][y];
3926     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3927     ChangeDelay[x][y] = 0;
3928     ChangePage[x][y] = -1;
3929     CustomValue[x][y] = 0;              // initialized in InitField()
3930     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3931     AmoebaNr[x][y] = 0;
3932     WasJustMoving[x][y] = 0;
3933     WasJustFalling[x][y] = 0;
3934     CheckCollision[x][y] = 0;
3935     CheckImpact[x][y] = 0;
3936     Stop[x][y] = FALSE;
3937     Pushed[x][y] = FALSE;
3938
3939     ChangeCount[x][y] = 0;
3940     ChangeEvent[x][y] = -1;
3941
3942     ExplodePhase[x][y] = 0;
3943     ExplodeDelay[x][y] = 0;
3944     ExplodeField[x][y] = EX_TYPE_NONE;
3945
3946     RunnerVisit[x][y] = 0;
3947     PlayerVisit[x][y] = 0;
3948
3949     GfxFrame[x][y] = 0;
3950     GfxRandom[x][y] = INIT_GFX_RANDOM();
3951     GfxRandomStatic[x][y] = INIT_GFX_RANDOM();
3952     GfxElement[x][y] = EL_UNDEFINED;
3953     GfxElementEmpty[x][y] = EL_EMPTY;
3954     GfxAction[x][y] = ACTION_DEFAULT;
3955     GfxDir[x][y] = MV_NONE;
3956     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3957   }
3958
3959   SCAN_PLAYFIELD(x, y)
3960   {
3961     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3962       emulate_bd = FALSE;
3963     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3964       emulate_sp = FALSE;
3965
3966     InitField(x, y, TRUE);
3967
3968     ResetGfxAnimation(x, y);
3969   }
3970
3971   InitBeltMovement();
3972
3973   for (i = 0; i < MAX_PLAYERS; i++)
3974   {
3975     struct PlayerInfo *player = &stored_player[i];
3976
3977     // set number of special actions for bored and sleeping animation
3978     player->num_special_action_bored =
3979       get_num_special_action(player->artwork_element,
3980                              ACTION_BORING_1, ACTION_BORING_LAST);
3981     player->num_special_action_sleeping =
3982       get_num_special_action(player->artwork_element,
3983                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3984   }
3985
3986   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3987                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3988
3989   // initialize type of slippery elements
3990   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3991   {
3992     if (!IS_CUSTOM_ELEMENT(i))
3993     {
3994       // default: elements slip down either to the left or right randomly
3995       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3996
3997       // SP style elements prefer to slip down on the left side
3998       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3999         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4000
4001       // BD style elements prefer to slip down on the left side
4002       if (game.emulation == EMU_BOULDERDASH)
4003         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4004     }
4005   }
4006
4007   // initialize explosion and ignition delay
4008   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4009   {
4010     if (!IS_CUSTOM_ELEMENT(i))
4011     {
4012       int num_phase = 8;
4013       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4014                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4015                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
4016       int last_phase = (num_phase + 1) * delay;
4017       int half_phase = (num_phase / 2) * delay;
4018
4019       element_info[i].explosion_delay = last_phase - 1;
4020       element_info[i].ignition_delay = half_phase;
4021
4022       if (i == EL_BLACK_ORB)
4023         element_info[i].ignition_delay = 1;
4024     }
4025   }
4026
4027   // correct non-moving belts to start moving left
4028   for (i = 0; i < NUM_BELTS; i++)
4029     if (game.belt_dir[i] == MV_NONE)
4030       game.belt_dir_nr[i] = 3;          // not moving, next moving left
4031
4032 #if USE_NEW_PLAYER_ASSIGNMENTS
4033   // use preferred player also in local single-player mode
4034   if (!network.enabled && !game.team_mode)
4035   {
4036     int new_index_nr = setup.network_player_nr;
4037
4038     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
4039     {
4040       for (i = 0; i < MAX_PLAYERS; i++)
4041         stored_player[i].connected_locally = FALSE;
4042
4043       stored_player[new_index_nr].connected_locally = TRUE;
4044     }
4045   }
4046
4047   for (i = 0; i < MAX_PLAYERS; i++)
4048   {
4049     stored_player[i].connected = FALSE;
4050
4051     // in network game mode, the local player might not be the first player
4052     if (stored_player[i].connected_locally)
4053       local_player = &stored_player[i];
4054   }
4055
4056   if (!network.enabled)
4057     local_player->connected = TRUE;
4058
4059   if (tape.playing)
4060   {
4061     for (i = 0; i < MAX_PLAYERS; i++)
4062       stored_player[i].connected = tape.player_participates[i];
4063   }
4064   else if (network.enabled)
4065   {
4066     // add team mode players connected over the network (needed for correct
4067     // assignment of player figures from level to locally playing players)
4068
4069     for (i = 0; i < MAX_PLAYERS; i++)
4070       if (stored_player[i].connected_network)
4071         stored_player[i].connected = TRUE;
4072   }
4073   else if (game.team_mode)
4074   {
4075     // try to guess locally connected team mode players (needed for correct
4076     // assignment of player figures from level to locally playing players)
4077
4078     for (i = 0; i < MAX_PLAYERS; i++)
4079       if (setup.input[i].use_joystick ||
4080           setup.input[i].key.left != KSYM_UNDEFINED)
4081         stored_player[i].connected = TRUE;
4082   }
4083
4084 #if DEBUG_INIT_PLAYER
4085   DebugPrintPlayerStatus("Player status after level initialization");
4086 #endif
4087
4088 #if DEBUG_INIT_PLAYER
4089   Debug("game:init:player", "Reassigning players ...");
4090 #endif
4091
4092   // check if any connected player was not found in playfield
4093   for (i = 0; i < MAX_PLAYERS; i++)
4094   {
4095     struct PlayerInfo *player = &stored_player[i];
4096
4097     if (player->connected && !player->present)
4098     {
4099       struct PlayerInfo *field_player = NULL;
4100
4101 #if DEBUG_INIT_PLAYER
4102       Debug("game:init:player",
4103             "- looking for field player for player %d ...", i + 1);
4104 #endif
4105
4106       // assign first free player found that is present in the playfield
4107
4108       // first try: look for unmapped playfield player that is not connected
4109       for (j = 0; j < MAX_PLAYERS; j++)
4110         if (field_player == NULL &&
4111             stored_player[j].present &&
4112             !stored_player[j].mapped &&
4113             !stored_player[j].connected)
4114           field_player = &stored_player[j];
4115
4116       // second try: look for *any* unmapped playfield player
4117       for (j = 0; j < MAX_PLAYERS; j++)
4118         if (field_player == NULL &&
4119             stored_player[j].present &&
4120             !stored_player[j].mapped)
4121           field_player = &stored_player[j];
4122
4123       if (field_player != NULL)
4124       {
4125         int jx = field_player->jx, jy = field_player->jy;
4126
4127 #if DEBUG_INIT_PLAYER
4128         Debug("game:init:player", "- found player %d",
4129               field_player->index_nr + 1);
4130 #endif
4131
4132         player->present = FALSE;
4133         player->active = FALSE;
4134
4135         field_player->present = TRUE;
4136         field_player->active = TRUE;
4137
4138         /*
4139         player->initial_element = field_player->initial_element;
4140         player->artwork_element = field_player->artwork_element;
4141
4142         player->block_last_field       = field_player->block_last_field;
4143         player->block_delay_adjustment = field_player->block_delay_adjustment;
4144         */
4145
4146         StorePlayer[jx][jy] = field_player->element_nr;
4147
4148         field_player->jx = field_player->last_jx = jx;
4149         field_player->jy = field_player->last_jy = jy;
4150
4151         if (local_player == player)
4152           local_player = field_player;
4153
4154         map_player_action[field_player->index_nr] = i;
4155
4156         field_player->mapped = TRUE;
4157
4158 #if DEBUG_INIT_PLAYER
4159         Debug("game:init:player", "- map_player_action[%d] == %d",
4160               field_player->index_nr + 1, i + 1);
4161 #endif
4162       }
4163     }
4164
4165     if (player->connected && player->present)
4166       player->mapped = TRUE;
4167   }
4168
4169 #if DEBUG_INIT_PLAYER
4170   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4171 #endif
4172
4173 #else
4174
4175   // check if any connected player was not found in playfield
4176   for (i = 0; i < MAX_PLAYERS; i++)
4177   {
4178     struct PlayerInfo *player = &stored_player[i];
4179
4180     if (player->connected && !player->present)
4181     {
4182       for (j = 0; j < MAX_PLAYERS; j++)
4183       {
4184         struct PlayerInfo *field_player = &stored_player[j];
4185         int jx = field_player->jx, jy = field_player->jy;
4186
4187         // assign first free player found that is present in the playfield
4188         if (field_player->present && !field_player->connected)
4189         {
4190           player->present = TRUE;
4191           player->active = TRUE;
4192
4193           field_player->present = FALSE;
4194           field_player->active = FALSE;
4195
4196           player->initial_element = field_player->initial_element;
4197           player->artwork_element = field_player->artwork_element;
4198
4199           player->block_last_field       = field_player->block_last_field;
4200           player->block_delay_adjustment = field_player->block_delay_adjustment;
4201
4202           StorePlayer[jx][jy] = player->element_nr;
4203
4204           player->jx = player->last_jx = jx;
4205           player->jy = player->last_jy = jy;
4206
4207           break;
4208         }
4209       }
4210     }
4211   }
4212 #endif
4213
4214 #if 0
4215   Debug("game:init:player", "local_player->present == %d",
4216         local_player->present);
4217 #endif
4218
4219   // set focus to local player for network games, else to all players
4220   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4221   game.centered_player_nr_next = game.centered_player_nr;
4222   game.set_centered_player = FALSE;
4223   game.set_centered_player_wrap = FALSE;
4224
4225   if (network_playing && tape.recording)
4226   {
4227     // store client dependent player focus when recording network games
4228     tape.centered_player_nr_next = game.centered_player_nr_next;
4229     tape.set_centered_player = TRUE;
4230   }
4231
4232   if (tape.playing)
4233   {
4234     // when playing a tape, eliminate all players who do not participate
4235
4236 #if USE_NEW_PLAYER_ASSIGNMENTS
4237
4238     if (!game.team_mode)
4239     {
4240       for (i = 0; i < MAX_PLAYERS; i++)
4241       {
4242         if (stored_player[i].active &&
4243             !tape.player_participates[map_player_action[i]])
4244         {
4245           struct PlayerInfo *player = &stored_player[i];
4246           int jx = player->jx, jy = player->jy;
4247
4248 #if DEBUG_INIT_PLAYER
4249           Debug("game:init:player", "Removing player %d at (%d, %d)",
4250                 i + 1, jx, jy);
4251 #endif
4252
4253           player->active = FALSE;
4254           StorePlayer[jx][jy] = 0;
4255           Tile[jx][jy] = EL_EMPTY;
4256         }
4257       }
4258     }
4259
4260 #else
4261
4262     for (i = 0; i < MAX_PLAYERS; i++)
4263     {
4264       if (stored_player[i].active &&
4265           !tape.player_participates[i])
4266       {
4267         struct PlayerInfo *player = &stored_player[i];
4268         int jx = player->jx, jy = player->jy;
4269
4270         player->active = FALSE;
4271         StorePlayer[jx][jy] = 0;
4272         Tile[jx][jy] = EL_EMPTY;
4273       }
4274     }
4275 #endif
4276   }
4277   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4278   {
4279     // when in single player mode, eliminate all but the local player
4280
4281     for (i = 0; i < MAX_PLAYERS; i++)
4282     {
4283       struct PlayerInfo *player = &stored_player[i];
4284
4285       if (player->active && player != local_player)
4286       {
4287         int jx = player->jx, jy = player->jy;
4288
4289         player->active = FALSE;
4290         player->present = FALSE;
4291
4292         StorePlayer[jx][jy] = 0;
4293         Tile[jx][jy] = EL_EMPTY;
4294       }
4295     }
4296   }
4297
4298   for (i = 0; i < MAX_PLAYERS; i++)
4299     if (stored_player[i].active)
4300       game.players_still_needed++;
4301
4302   if (level.solved_by_one_player)
4303     game.players_still_needed = 1;
4304
4305   // when recording the game, store which players take part in the game
4306   if (tape.recording)
4307   {
4308 #if USE_NEW_PLAYER_ASSIGNMENTS
4309     for (i = 0; i < MAX_PLAYERS; i++)
4310       if (stored_player[i].connected)
4311         tape.player_participates[i] = TRUE;
4312 #else
4313     for (i = 0; i < MAX_PLAYERS; i++)
4314       if (stored_player[i].active)
4315         tape.player_participates[i] = TRUE;
4316 #endif
4317   }
4318
4319 #if DEBUG_INIT_PLAYER
4320   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4321 #endif
4322
4323   if (BorderElement == EL_EMPTY)
4324   {
4325     SBX_Left = 0;
4326     SBX_Right = lev_fieldx - SCR_FIELDX;
4327     SBY_Upper = 0;
4328     SBY_Lower = lev_fieldy - SCR_FIELDY;
4329   }
4330   else
4331   {
4332     SBX_Left = -1;
4333     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4334     SBY_Upper = -1;
4335     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4336   }
4337
4338   if (full_lev_fieldx <= SCR_FIELDX)
4339     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4340   if (full_lev_fieldy <= SCR_FIELDY)
4341     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4342
4343   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4344     SBX_Left--;
4345   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4346     SBY_Upper--;
4347
4348   // if local player not found, look for custom element that might create
4349   // the player (make some assumptions about the right custom element)
4350   if (!local_player->present)
4351   {
4352     int start_x = 0, start_y = 0;
4353     int found_rating = 0;
4354     int found_element = EL_UNDEFINED;
4355     int player_nr = local_player->index_nr;
4356
4357     SCAN_PLAYFIELD(x, y)
4358     {
4359       int element = Tile[x][y];
4360       int content;
4361       int xx, yy;
4362       boolean is_player;
4363
4364       if (level.use_start_element[player_nr] &&
4365           level.start_element[player_nr] == element &&
4366           found_rating < 4)
4367       {
4368         start_x = x;
4369         start_y = y;
4370
4371         found_rating = 4;
4372         found_element = element;
4373       }
4374
4375       if (!IS_CUSTOM_ELEMENT(element))
4376         continue;
4377
4378       if (CAN_CHANGE(element))
4379       {
4380         for (i = 0; i < element_info[element].num_change_pages; i++)
4381         {
4382           // check for player created from custom element as single target
4383           content = element_info[element].change_page[i].target_element;
4384           is_player = IS_PLAYER_ELEMENT(content);
4385
4386           if (is_player && (found_rating < 3 ||
4387                             (found_rating == 3 && element < found_element)))
4388           {
4389             start_x = x;
4390             start_y = y;
4391
4392             found_rating = 3;
4393             found_element = element;
4394           }
4395         }
4396       }
4397
4398       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4399       {
4400         // check for player created from custom element as explosion content
4401         content = element_info[element].content.e[xx][yy];
4402         is_player = IS_PLAYER_ELEMENT(content);
4403
4404         if (is_player && (found_rating < 2 ||
4405                           (found_rating == 2 && element < found_element)))
4406         {
4407           start_x = x + xx - 1;
4408           start_y = y + yy - 1;
4409
4410           found_rating = 2;
4411           found_element = element;
4412         }
4413
4414         if (!CAN_CHANGE(element))
4415           continue;
4416
4417         for (i = 0; i < element_info[element].num_change_pages; i++)
4418         {
4419           // check for player created from custom element as extended target
4420           content =
4421             element_info[element].change_page[i].target_content.e[xx][yy];
4422
4423           is_player = IS_PLAYER_ELEMENT(content);
4424
4425           if (is_player && (found_rating < 1 ||
4426                             (found_rating == 1 && element < found_element)))
4427           {
4428             start_x = x + xx - 1;
4429             start_y = y + yy - 1;
4430
4431             found_rating = 1;
4432             found_element = element;
4433           }
4434         }
4435       }
4436     }
4437
4438     scroll_x = SCROLL_POSITION_X(start_x);
4439     scroll_y = SCROLL_POSITION_Y(start_y);
4440   }
4441   else
4442   {
4443     scroll_x = SCROLL_POSITION_X(local_player->jx);
4444     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4445   }
4446
4447   // !!! FIX THIS (START) !!!
4448   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4449   {
4450     InitGameEngine_EM();
4451   }
4452   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4453   {
4454     InitGameEngine_SP();
4455   }
4456   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4457   {
4458     InitGameEngine_MM();
4459   }
4460   else
4461   {
4462     DrawLevel(REDRAW_FIELD);
4463     DrawAllPlayers();
4464
4465     // after drawing the level, correct some elements
4466     if (game.timegate_time_left == 0)
4467       CloseAllOpenTimegates();
4468   }
4469
4470   // blit playfield from scroll buffer to normal back buffer for fading in
4471   BlitScreenToBitmap(backbuffer);
4472   // !!! FIX THIS (END) !!!
4473
4474   DrawMaskedBorder(fade_mask);
4475
4476   FadeIn(fade_mask);
4477
4478 #if 1
4479   // full screen redraw is required at this point in the following cases:
4480   // - special editor door undrawn when game was started from level editor
4481   // - drawing area (playfield) was changed and has to be removed completely
4482   redraw_mask = REDRAW_ALL;
4483   BackToFront();
4484 #endif
4485
4486   if (!game.restart_level)
4487   {
4488     // copy default game door content to main double buffer
4489
4490     // !!! CHECK AGAIN !!!
4491     SetPanelBackground();
4492     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4493     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4494   }
4495
4496   SetPanelBackground();
4497   SetDrawBackgroundMask(REDRAW_DOOR_1);
4498
4499   UpdateAndDisplayGameControlValues();
4500
4501   if (!game.restart_level)
4502   {
4503     UnmapGameButtons();
4504     UnmapTapeButtons();
4505
4506     FreeGameButtons();
4507     CreateGameButtons();
4508
4509     MapGameButtons();
4510     MapTapeButtons();
4511
4512     // copy actual game door content to door double buffer for OpenDoor()
4513     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4514
4515     OpenDoor(DOOR_OPEN_ALL);
4516
4517     KeyboardAutoRepeatOffUnlessAutoplay();
4518
4519 #if DEBUG_INIT_PLAYER
4520     DebugPrintPlayerStatus("Player status (final)");
4521 #endif
4522   }
4523
4524   UnmapAllGadgets();
4525
4526   MapGameButtons();
4527   MapTapeButtons();
4528
4529   if (!game.restart_level && !tape.playing)
4530   {
4531     LevelStats_incPlayed(level_nr);
4532
4533     SaveLevelSetup_SeriesInfo();
4534   }
4535
4536   game.restart_level = FALSE;
4537
4538   game.request_active = FALSE;
4539   game.request_active_or_moving = FALSE;
4540
4541   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4542     InitGameActions_MM();
4543
4544   SaveEngineSnapshotToListInitial();
4545
4546   if (!game.restart_level)
4547   {
4548     PlaySound(SND_GAME_STARTING);
4549
4550     if (setup.sound_music)
4551       PlayLevelMusic();
4552   }
4553
4554   SetPlayfieldMouseCursorEnabled(!game.use_mouse_actions);
4555 }
4556
4557 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4558                         int actual_player_x, int actual_player_y)
4559 {
4560   // this is used for non-R'n'D game engines to update certain engine values
4561
4562   // needed to determine if sounds are played within the visible screen area
4563   scroll_x = actual_scroll_x;
4564   scroll_y = actual_scroll_y;
4565
4566   // needed to get player position for "follow finger" playing input method
4567   local_player->jx = actual_player_x;
4568   local_player->jy = actual_player_y;
4569 }
4570
4571 void InitMovDir(int x, int y)
4572 {
4573   int i, element = Tile[x][y];
4574   static int xy[4][2] =
4575   {
4576     {  0, +1 },
4577     { +1,  0 },
4578     {  0, -1 },
4579     { -1,  0 }
4580   };
4581   static int direction[3][4] =
4582   {
4583     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4584     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4585     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4586   };
4587
4588   switch (element)
4589   {
4590     case EL_BUG_RIGHT:
4591     case EL_BUG_UP:
4592     case EL_BUG_LEFT:
4593     case EL_BUG_DOWN:
4594       Tile[x][y] = EL_BUG;
4595       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4596       break;
4597
4598     case EL_SPACESHIP_RIGHT:
4599     case EL_SPACESHIP_UP:
4600     case EL_SPACESHIP_LEFT:
4601     case EL_SPACESHIP_DOWN:
4602       Tile[x][y] = EL_SPACESHIP;
4603       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4604       break;
4605
4606     case EL_BD_BUTTERFLY_RIGHT:
4607     case EL_BD_BUTTERFLY_UP:
4608     case EL_BD_BUTTERFLY_LEFT:
4609     case EL_BD_BUTTERFLY_DOWN:
4610       Tile[x][y] = EL_BD_BUTTERFLY;
4611       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4612       break;
4613
4614     case EL_BD_FIREFLY_RIGHT:
4615     case EL_BD_FIREFLY_UP:
4616     case EL_BD_FIREFLY_LEFT:
4617     case EL_BD_FIREFLY_DOWN:
4618       Tile[x][y] = EL_BD_FIREFLY;
4619       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4620       break;
4621
4622     case EL_PACMAN_RIGHT:
4623     case EL_PACMAN_UP:
4624     case EL_PACMAN_LEFT:
4625     case EL_PACMAN_DOWN:
4626       Tile[x][y] = EL_PACMAN;
4627       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4628       break;
4629
4630     case EL_YAMYAM_LEFT:
4631     case EL_YAMYAM_RIGHT:
4632     case EL_YAMYAM_UP:
4633     case EL_YAMYAM_DOWN:
4634       Tile[x][y] = EL_YAMYAM;
4635       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4636       break;
4637
4638     case EL_SP_SNIKSNAK:
4639       MovDir[x][y] = MV_UP;
4640       break;
4641
4642     case EL_SP_ELECTRON:
4643       MovDir[x][y] = MV_LEFT;
4644       break;
4645
4646     case EL_MOLE_LEFT:
4647     case EL_MOLE_RIGHT:
4648     case EL_MOLE_UP:
4649     case EL_MOLE_DOWN:
4650       Tile[x][y] = EL_MOLE;
4651       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4652       break;
4653
4654     case EL_SPRING_LEFT:
4655     case EL_SPRING_RIGHT:
4656       Tile[x][y] = EL_SPRING;
4657       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4658       break;
4659
4660     default:
4661       if (IS_CUSTOM_ELEMENT(element))
4662       {
4663         struct ElementInfo *ei = &element_info[element];
4664         int move_direction_initial = ei->move_direction_initial;
4665         int move_pattern = ei->move_pattern;
4666
4667         if (move_direction_initial == MV_START_PREVIOUS)
4668         {
4669           if (MovDir[x][y] != MV_NONE)
4670             return;
4671
4672           move_direction_initial = MV_START_AUTOMATIC;
4673         }
4674
4675         if (move_direction_initial == MV_START_RANDOM)
4676           MovDir[x][y] = 1 << RND(4);
4677         else if (move_direction_initial & MV_ANY_DIRECTION)
4678           MovDir[x][y] = move_direction_initial;
4679         else if (move_pattern == MV_ALL_DIRECTIONS ||
4680                  move_pattern == MV_TURNING_LEFT ||
4681                  move_pattern == MV_TURNING_RIGHT ||
4682                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4683                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4684                  move_pattern == MV_TURNING_RANDOM)
4685           MovDir[x][y] = 1 << RND(4);
4686         else if (move_pattern == MV_HORIZONTAL)
4687           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4688         else if (move_pattern == MV_VERTICAL)
4689           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4690         else if (move_pattern & MV_ANY_DIRECTION)
4691           MovDir[x][y] = element_info[element].move_pattern;
4692         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4693                  move_pattern == MV_ALONG_RIGHT_SIDE)
4694         {
4695           // use random direction as default start direction
4696           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4697             MovDir[x][y] = 1 << RND(4);
4698
4699           for (i = 0; i < NUM_DIRECTIONS; i++)
4700           {
4701             int x1 = x + xy[i][0];
4702             int y1 = y + xy[i][1];
4703
4704             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4705             {
4706               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4707                 MovDir[x][y] = direction[0][i];
4708               else
4709                 MovDir[x][y] = direction[1][i];
4710
4711               break;
4712             }
4713           }
4714         }                
4715       }
4716       else
4717       {
4718         MovDir[x][y] = 1 << RND(4);
4719
4720         if (element != EL_BUG &&
4721             element != EL_SPACESHIP &&
4722             element != EL_BD_BUTTERFLY &&
4723             element != EL_BD_FIREFLY)
4724           break;
4725
4726         for (i = 0; i < NUM_DIRECTIONS; i++)
4727         {
4728           int x1 = x + xy[i][0];
4729           int y1 = y + xy[i][1];
4730
4731           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4732           {
4733             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4734             {
4735               MovDir[x][y] = direction[0][i];
4736               break;
4737             }
4738             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4739                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4740             {
4741               MovDir[x][y] = direction[1][i];
4742               break;
4743             }
4744           }
4745         }
4746       }
4747       break;
4748   }
4749
4750   GfxDir[x][y] = MovDir[x][y];
4751 }
4752
4753 void InitAmoebaNr(int x, int y)
4754 {
4755   int i;
4756   int group_nr = AmoebaNeighbourNr(x, y);
4757
4758   if (group_nr == 0)
4759   {
4760     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4761     {
4762       if (AmoebaCnt[i] == 0)
4763       {
4764         group_nr = i;
4765         break;
4766       }
4767     }
4768   }
4769
4770   AmoebaNr[x][y] = group_nr;
4771   AmoebaCnt[group_nr]++;
4772   AmoebaCnt2[group_nr]++;
4773 }
4774
4775 static void LevelSolved_SetFinalGameValues(void)
4776 {
4777   game.time_final = (game.no_level_time_limit ? TimePlayed : TimeLeft);
4778   game.score_time_final = (level.use_step_counter ? TimePlayed :
4779                            TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4780
4781   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4782                       game_em.lev->score :
4783                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4784                       game_mm.score :
4785                       game.score);
4786
4787   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4788                        MM_HEALTH(game_mm.laser_overload_value) :
4789                        game.health);
4790
4791   game.LevelSolved_CountingTime = game.time_final;
4792   game.LevelSolved_CountingScore = game.score_final;
4793   game.LevelSolved_CountingHealth = game.health_final;
4794 }
4795
4796 static void LevelSolved_DisplayFinalGameValues(int time, int score, int health)
4797 {
4798   game.LevelSolved_CountingTime = time;
4799   game.LevelSolved_CountingScore = score;
4800   game.LevelSolved_CountingHealth = health;
4801
4802   game_panel_controls[GAME_PANEL_TIME].value = time;
4803   game_panel_controls[GAME_PANEL_SCORE].value = score;
4804   game_panel_controls[GAME_PANEL_HEALTH].value = health;
4805
4806   DisplayGameControlValues();
4807 }
4808
4809 static void LevelSolved(void)
4810 {
4811   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4812       game.players_still_needed > 0)
4813     return;
4814
4815   game.LevelSolved = TRUE;
4816   game.GameOver = TRUE;
4817
4818   tape.solved = TRUE;
4819
4820   // needed here to display correct panel values while player walks into exit
4821   LevelSolved_SetFinalGameValues();
4822 }
4823
4824 void GameWon(void)
4825 {
4826   static int time_count_steps;
4827   static int time, time_final;
4828   static float score, score_final; // needed for time score < 10 for 10 seconds
4829   static int health, health_final;
4830   static int game_over_delay_1 = 0;
4831   static int game_over_delay_2 = 0;
4832   static int game_over_delay_3 = 0;
4833   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4834   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4835
4836   if (!game.LevelSolved_GameWon)
4837   {
4838     int i;
4839
4840     // do not start end game actions before the player stops moving (to exit)
4841     if (local_player->active && local_player->MovPos)
4842       return;
4843
4844     // calculate final game values after player finished walking into exit
4845     LevelSolved_SetFinalGameValues();
4846
4847     game.LevelSolved_GameWon = TRUE;
4848     game.LevelSolved_SaveTape = tape.recording;
4849     game.LevelSolved_SaveScore = !tape.playing;
4850
4851     if (!tape.playing)
4852     {
4853       LevelStats_incSolved(level_nr);
4854
4855       SaveLevelSetup_SeriesInfo();
4856     }
4857
4858     if (tape.auto_play)         // tape might already be stopped here
4859       tape.auto_play_level_solved = TRUE;
4860
4861     TapeStop();
4862
4863     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4864     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4865     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4866
4867     time = time_final = game.time_final;
4868     score = score_final = game.score_final;
4869     health = health_final = game.health_final;
4870
4871     // update game panel values before (delayed) counting of score (if any)
4872     LevelSolved_DisplayFinalGameValues(time, score, health);
4873
4874     // if level has time score defined, calculate new final game values
4875     if (time_score > 0)
4876     {
4877       int time_final_max = 999;
4878       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4879       int time_frames = 0;
4880       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4881       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4882
4883       if (TimeLeft > 0)
4884       {
4885         time_final = 0;
4886         time_frames = time_frames_left;
4887       }
4888       else if (game.no_level_time_limit && TimePlayed < time_final_max)
4889       {
4890         time_final = time_final_max;
4891         time_frames = time_frames_final_max - time_frames_played;
4892       }
4893
4894       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4895
4896       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4897
4898       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4899       {
4900         health_final = 0;
4901         score_final += health * time_score;
4902       }
4903
4904       game.score_final = score_final;
4905       game.health_final = health_final;
4906     }
4907
4908     // if not counting score after game, immediately update game panel values
4909     if (level_editor_test_game || !setup.count_score_after_game)
4910     {
4911       time = time_final;
4912       score = score_final;
4913
4914       LevelSolved_DisplayFinalGameValues(time, score, health);
4915     }
4916
4917     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4918     {
4919       // check if last player has left the level
4920       if (game.exit_x >= 0 &&
4921           game.exit_y >= 0)
4922       {
4923         int x = game.exit_x;
4924         int y = game.exit_y;
4925         int element = Tile[x][y];
4926
4927         // close exit door after last player
4928         if ((game.all_players_gone &&
4929              (element == EL_EXIT_OPEN ||
4930               element == EL_SP_EXIT_OPEN ||
4931               element == EL_STEEL_EXIT_OPEN)) ||
4932             element == EL_EM_EXIT_OPEN ||
4933             element == EL_EM_STEEL_EXIT_OPEN)
4934         {
4935
4936           Tile[x][y] =
4937             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4938              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4939              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4940              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4941              EL_EM_STEEL_EXIT_CLOSING);
4942
4943           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4944         }
4945
4946         // player disappears
4947         DrawLevelField(x, y);
4948       }
4949
4950       for (i = 0; i < MAX_PLAYERS; i++)
4951       {
4952         struct PlayerInfo *player = &stored_player[i];
4953
4954         if (player->present)
4955         {
4956           RemovePlayer(player);
4957
4958           // player disappears
4959           DrawLevelField(player->jx, player->jy);
4960         }
4961       }
4962     }
4963
4964     PlaySound(SND_GAME_WINNING);
4965   }
4966
4967   if (setup.count_score_after_game)
4968   {
4969     if (time != time_final)
4970     {
4971       if (game_over_delay_1 > 0)
4972       {
4973         game_over_delay_1--;
4974
4975         return;
4976       }
4977
4978       int time_to_go = ABS(time_final - time);
4979       int time_count_dir = (time < time_final ? +1 : -1);
4980
4981       if (time_to_go < time_count_steps)
4982         time_count_steps = 1;
4983
4984       time  += time_count_steps * time_count_dir;
4985       score += time_count_steps * time_score;
4986
4987       // set final score to correct rounding differences after counting score
4988       if (time == time_final)
4989         score = score_final;
4990
4991       LevelSolved_DisplayFinalGameValues(time, score, health);
4992
4993       if (time == time_final)
4994         StopSound(SND_GAME_LEVELTIME_BONUS);
4995       else if (setup.sound_loops)
4996         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4997       else
4998         PlaySound(SND_GAME_LEVELTIME_BONUS);
4999
5000       return;
5001     }
5002
5003     if (health != health_final)
5004     {
5005       if (game_over_delay_2 > 0)
5006       {
5007         game_over_delay_2--;
5008
5009         return;
5010       }
5011
5012       int health_count_dir = (health < health_final ? +1 : -1);
5013
5014       health += health_count_dir;
5015       score  += time_score;
5016
5017       LevelSolved_DisplayFinalGameValues(time, score, health);
5018
5019       if (health == health_final)
5020         StopSound(SND_GAME_LEVELTIME_BONUS);
5021       else if (setup.sound_loops)
5022         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5023       else
5024         PlaySound(SND_GAME_LEVELTIME_BONUS);
5025
5026       return;
5027     }
5028   }
5029
5030   game.panel.active = FALSE;
5031
5032   if (game_over_delay_3 > 0)
5033   {
5034     game_over_delay_3--;
5035
5036     return;
5037   }
5038
5039   GameEnd();
5040 }
5041
5042 void GameEnd(void)
5043 {
5044   // used instead of "level_nr" (needed for network games)
5045   int last_level_nr = levelset.level_nr;
5046   boolean tape_saved = FALSE;
5047
5048   game.LevelSolved_GameEnd = TRUE;
5049
5050   if (game.LevelSolved_SaveTape && !score_info_tape_play)
5051   {
5052     // make sure that request dialog to save tape does not open door again
5053     if (!global.use_envelope_request)
5054       CloseDoor(DOOR_CLOSE_1);
5055
5056     // ask to save tape
5057     tape_saved = SaveTapeChecked_LevelSolved(tape.level_nr);
5058
5059     // set unique basename for score tape (also saved in high score table)
5060     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
5061   }
5062
5063   // if no tape is to be saved, close both doors simultaneously
5064   CloseDoor(DOOR_CLOSE_ALL);
5065
5066   if (level_editor_test_game || score_info_tape_play)
5067   {
5068     SetGameStatus(GAME_MODE_MAIN);
5069
5070     DrawMainMenu();
5071
5072     return;
5073   }
5074
5075   if (!game.LevelSolved_SaveScore)
5076   {
5077     SetGameStatus(GAME_MODE_MAIN);
5078
5079     DrawMainMenu();
5080
5081     return;
5082   }
5083
5084   if (level_nr == leveldir_current->handicap_level)
5085   {
5086     leveldir_current->handicap_level++;
5087
5088     SaveLevelSetup_SeriesInfo();
5089   }
5090
5091   // save score and score tape before potentially erasing tape below
5092   NewHighScore(last_level_nr, tape_saved);
5093
5094   if (setup.increment_levels &&
5095       level_nr < leveldir_current->last_level &&
5096       !network_playing)
5097   {
5098     level_nr++;         // advance to next level
5099     TapeErase();        // start with empty tape
5100
5101     if (setup.auto_play_next_level)
5102     {
5103       scores.continue_playing = TRUE;
5104       scores.next_level_nr = level_nr;
5105
5106       LoadLevel(level_nr);
5107
5108       SaveLevelSetup_SeriesInfo();
5109     }
5110   }
5111
5112   if (scores.last_added >= 0 && setup.show_scores_after_game)
5113   {
5114     SetGameStatus(GAME_MODE_SCORES);
5115
5116     DrawHallOfFame(last_level_nr);
5117   }
5118   else if (scores.continue_playing)
5119   {
5120     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5121   }
5122   else
5123   {
5124     SetGameStatus(GAME_MODE_MAIN);
5125
5126     DrawMainMenu();
5127   }
5128 }
5129
5130 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5131                          boolean one_score_entry_per_name)
5132 {
5133   int i;
5134
5135   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5136     return -1;
5137
5138   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5139   {
5140     struct ScoreEntry *entry = &list->entry[i];
5141     boolean score_is_better = (new_entry->score >  entry->score);
5142     boolean score_is_equal  = (new_entry->score == entry->score);
5143     boolean time_is_better  = (new_entry->time  <  entry->time);
5144     boolean time_is_equal   = (new_entry->time  == entry->time);
5145     boolean better_by_score = (score_is_better ||
5146                                (score_is_equal && time_is_better));
5147     boolean better_by_time  = (time_is_better ||
5148                                (time_is_equal && score_is_better));
5149     boolean is_better = (level.rate_time_over_score ? better_by_time :
5150                          better_by_score);
5151     boolean entry_is_empty = (entry->score == 0 &&
5152                               entry->time == 0);
5153
5154     // prevent adding server score entries if also existing in local score file
5155     // (special case: historic score entries have an empty tape basename entry)
5156     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5157         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5158     {
5159       // add fields from server score entry not stored in local score entry
5160       // (currently, this means setting platform, version and country fields;
5161       // in rare cases, this may also correct an invalid score value, as
5162       // historic scores might have been truncated to 16-bit values locally)
5163       *entry = *new_entry;
5164
5165       return -1;
5166     }
5167
5168     if (is_better || entry_is_empty)
5169     {
5170       // player has made it to the hall of fame
5171
5172       if (i < MAX_SCORE_ENTRIES - 1)
5173       {
5174         int m = MAX_SCORE_ENTRIES - 1;
5175         int l;
5176
5177         if (one_score_entry_per_name)
5178         {
5179           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5180             if (strEqual(list->entry[l].name, new_entry->name))
5181               m = l;
5182
5183           if (m == i)   // player's new highscore overwrites his old one
5184             goto put_into_list;
5185         }
5186
5187         for (l = m; l > i; l--)
5188           list->entry[l] = list->entry[l - 1];
5189       }
5190
5191       put_into_list:
5192
5193       *entry = *new_entry;
5194
5195       return i;
5196     }
5197     else if (one_score_entry_per_name &&
5198              strEqual(entry->name, new_entry->name))
5199     {
5200       // player already in high score list with better score or time
5201
5202       return -1;
5203     }
5204   }
5205
5206   // special case: new score is beyond the last high score list position
5207   return MAX_SCORE_ENTRIES;
5208 }
5209
5210 void NewHighScore(int level_nr, boolean tape_saved)
5211 {
5212   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5213   boolean one_per_name = FALSE;
5214
5215   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5216   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5217
5218   new_entry.score = game.score_final;
5219   new_entry.time = game.score_time_final;
5220
5221   LoadScore(level_nr);
5222
5223   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5224
5225   if (scores.last_added >= MAX_SCORE_ENTRIES)
5226   {
5227     scores.last_added = MAX_SCORE_ENTRIES - 1;
5228     scores.force_last_added = TRUE;
5229
5230     scores.entry[scores.last_added] = new_entry;
5231
5232     // store last added local score entry (before merging server scores)
5233     scores.last_added_local = scores.last_added;
5234
5235     return;
5236   }
5237
5238   if (scores.last_added < 0)
5239     return;
5240
5241   SaveScore(level_nr);
5242
5243   // store last added local score entry (before merging server scores)
5244   scores.last_added_local = scores.last_added;
5245
5246   if (!game.LevelSolved_SaveTape)
5247     return;
5248
5249   SaveScoreTape(level_nr);
5250
5251   if (setup.ask_for_using_api_server)
5252   {
5253     setup.use_api_server =
5254       Request("Upload your score and tape to the high score server?", REQ_ASK);
5255
5256     if (!setup.use_api_server)
5257       Request("Not using high score server! Use setup menu to enable again!",
5258               REQ_CONFIRM);
5259
5260     runtime.use_api_server = setup.use_api_server;
5261
5262     // after asking for using API server once, do not ask again
5263     setup.ask_for_using_api_server = FALSE;
5264
5265     SaveSetup_ServerSetup();
5266   }
5267
5268   SaveServerScore(level_nr, tape_saved);
5269 }
5270
5271 void MergeServerScore(void)
5272 {
5273   struct ScoreEntry last_added_entry;
5274   boolean one_per_name = FALSE;
5275   int i;
5276
5277   if (scores.last_added >= 0)
5278     last_added_entry = scores.entry[scores.last_added];
5279
5280   for (i = 0; i < server_scores.num_entries; i++)
5281   {
5282     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5283
5284     if (pos >= 0 && pos <= scores.last_added)
5285       scores.last_added++;
5286   }
5287
5288   if (scores.last_added >= MAX_SCORE_ENTRIES)
5289   {
5290     scores.last_added = MAX_SCORE_ENTRIES - 1;
5291     scores.force_last_added = TRUE;
5292
5293     scores.entry[scores.last_added] = last_added_entry;
5294   }
5295 }
5296
5297 static int getElementMoveStepsizeExt(int x, int y, int direction)
5298 {
5299   int element = Tile[x][y];
5300   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5301   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5302   int horiz_move = (dx != 0);
5303   int sign = (horiz_move ? dx : dy);
5304   int step = sign * element_info[element].move_stepsize;
5305
5306   // special values for move stepsize for spring and things on conveyor belt
5307   if (horiz_move)
5308   {
5309     if (CAN_FALL(element) &&
5310         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5311       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5312     else if (element == EL_SPRING)
5313       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5314   }
5315
5316   return step;
5317 }
5318
5319 static int getElementMoveStepsize(int x, int y)
5320 {
5321   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5322 }
5323
5324 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5325 {
5326   if (player->GfxAction != action || player->GfxDir != dir)
5327   {
5328     player->GfxAction = action;
5329     player->GfxDir = dir;
5330     player->Frame = 0;
5331     player->StepFrame = 0;
5332   }
5333 }
5334
5335 static void ResetGfxFrame(int x, int y)
5336 {
5337   // profiling showed that "autotest" spends 10~20% of its time in this function
5338   if (DrawingDeactivatedField())
5339     return;
5340
5341   int element = Tile[x][y];
5342   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5343
5344   if (graphic_info[graphic].anim_global_sync)
5345     GfxFrame[x][y] = FrameCounter;
5346   else if (graphic_info[graphic].anim_global_anim_sync)
5347     GfxFrame[x][y] = getGlobalAnimSyncFrame();
5348   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5349     GfxFrame[x][y] = CustomValue[x][y];
5350   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5351     GfxFrame[x][y] = element_info[element].collect_score;
5352   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5353     GfxFrame[x][y] = ChangeDelay[x][y];
5354 }
5355
5356 static void ResetGfxAnimation(int x, int y)
5357 {
5358   GfxAction[x][y] = ACTION_DEFAULT;
5359   GfxDir[x][y] = MovDir[x][y];
5360   GfxFrame[x][y] = 0;
5361
5362   ResetGfxFrame(x, y);
5363 }
5364
5365 static void ResetRandomAnimationValue(int x, int y)
5366 {
5367   GfxRandom[x][y] = INIT_GFX_RANDOM();
5368 }
5369
5370 static void InitMovingField(int x, int y, int direction)
5371 {
5372   int element = Tile[x][y];
5373   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5374   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5375   int newx = x + dx;
5376   int newy = y + dy;
5377   boolean is_moving_before, is_moving_after;
5378
5379   // check if element was/is moving or being moved before/after mode change
5380   is_moving_before = (WasJustMoving[x][y] != 0);
5381   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5382
5383   // reset animation only for moving elements which change direction of moving
5384   // or which just started or stopped moving
5385   // (else CEs with property "can move" / "not moving" are reset each frame)
5386   if (is_moving_before != is_moving_after ||
5387       direction != MovDir[x][y])
5388     ResetGfxAnimation(x, y);
5389
5390   MovDir[x][y] = direction;
5391   GfxDir[x][y] = direction;
5392
5393   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5394                      direction == MV_DOWN && CAN_FALL(element) ?
5395                      ACTION_FALLING : ACTION_MOVING);
5396
5397   // this is needed for CEs with property "can move" / "not moving"
5398
5399   if (is_moving_after)
5400   {
5401     if (Tile[newx][newy] == EL_EMPTY)
5402       Tile[newx][newy] = EL_BLOCKED;
5403
5404     MovDir[newx][newy] = MovDir[x][y];
5405
5406     CustomValue[newx][newy] = CustomValue[x][y];
5407
5408     GfxFrame[newx][newy] = GfxFrame[x][y];
5409     GfxRandom[newx][newy] = GfxRandom[x][y];
5410     GfxAction[newx][newy] = GfxAction[x][y];
5411     GfxDir[newx][newy] = GfxDir[x][y];
5412   }
5413 }
5414
5415 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5416 {
5417   int direction = MovDir[x][y];
5418   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5419   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5420
5421   *goes_to_x = newx;
5422   *goes_to_y = newy;
5423 }
5424
5425 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5426 {
5427   int direction = MovDir[x][y];
5428   int oldx = x + (direction & MV_LEFT ? +1 : direction & MV_RIGHT ? -1 : 0);
5429   int oldy = y + (direction & MV_UP   ? +1 : direction & MV_DOWN  ? -1 : 0);
5430
5431   *comes_from_x = oldx;
5432   *comes_from_y = oldy;
5433 }
5434
5435 static int MovingOrBlocked2Element(int x, int y)
5436 {
5437   int element = Tile[x][y];
5438
5439   if (element == EL_BLOCKED)
5440   {
5441     int oldx, oldy;
5442
5443     Blocked2Moving(x, y, &oldx, &oldy);
5444
5445     return Tile[oldx][oldy];
5446   }
5447
5448   return element;
5449 }
5450
5451 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5452 {
5453   // like MovingOrBlocked2Element(), but if element is moving
5454   // and (x, y) is the field the moving element is just leaving,
5455   // return EL_BLOCKED instead of the element value
5456   int element = Tile[x][y];
5457
5458   if (IS_MOVING(x, y))
5459   {
5460     if (element == EL_BLOCKED)
5461     {
5462       int oldx, oldy;
5463
5464       Blocked2Moving(x, y, &oldx, &oldy);
5465       return Tile[oldx][oldy];
5466     }
5467     else
5468       return EL_BLOCKED;
5469   }
5470   else
5471     return element;
5472 }
5473
5474 static void RemoveField(int x, int y)
5475 {
5476   Tile[x][y] = EL_EMPTY;
5477
5478   MovPos[x][y] = 0;
5479   MovDir[x][y] = 0;
5480   MovDelay[x][y] = 0;
5481
5482   CustomValue[x][y] = 0;
5483
5484   AmoebaNr[x][y] = 0;
5485   ChangeDelay[x][y] = 0;
5486   ChangePage[x][y] = -1;
5487   Pushed[x][y] = FALSE;
5488
5489   GfxElement[x][y] = EL_UNDEFINED;
5490   GfxAction[x][y] = ACTION_DEFAULT;
5491   GfxDir[x][y] = MV_NONE;
5492 }
5493
5494 static void RemoveMovingField(int x, int y)
5495 {
5496   int oldx = x, oldy = y, newx = x, newy = y;
5497   int element = Tile[x][y];
5498   int next_element = EL_UNDEFINED;
5499
5500   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5501     return;
5502
5503   if (IS_MOVING(x, y))
5504   {
5505     Moving2Blocked(x, y, &newx, &newy);
5506
5507     if (Tile[newx][newy] != EL_BLOCKED)
5508     {
5509       // element is moving, but target field is not free (blocked), but
5510       // already occupied by something different (example: acid pool);
5511       // in this case, only remove the moving field, but not the target
5512
5513       RemoveField(oldx, oldy);
5514
5515       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5516
5517       TEST_DrawLevelField(oldx, oldy);
5518
5519       return;
5520     }
5521   }
5522   else if (element == EL_BLOCKED)
5523   {
5524     Blocked2Moving(x, y, &oldx, &oldy);
5525     if (!IS_MOVING(oldx, oldy))
5526       return;
5527   }
5528
5529   if (element == EL_BLOCKED &&
5530       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5531        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5532        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5533        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5534        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5535        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5536     next_element = get_next_element(Tile[oldx][oldy]);
5537
5538   RemoveField(oldx, oldy);
5539   RemoveField(newx, newy);
5540
5541   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5542
5543   if (next_element != EL_UNDEFINED)
5544     Tile[oldx][oldy] = next_element;
5545
5546   TEST_DrawLevelField(oldx, oldy);
5547   TEST_DrawLevelField(newx, newy);
5548 }
5549
5550 void DrawDynamite(int x, int y)
5551 {
5552   int sx = SCREENX(x), sy = SCREENY(y);
5553   int graphic = el2img(Tile[x][y]);
5554   int frame;
5555
5556   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5557     return;
5558
5559   if (IS_WALKABLE_INSIDE(Back[x][y]))
5560     return;
5561
5562   if (Back[x][y])
5563     DrawLevelElement(x, y, Back[x][y]);
5564   else if (Store[x][y])
5565     DrawLevelElement(x, y, Store[x][y]);
5566   else if (game.use_masked_elements)
5567     DrawLevelElement(x, y, EL_EMPTY);
5568
5569   frame = getGraphicAnimationFrameXY(graphic, x, y);
5570
5571   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5572     DrawGraphicThruMask(sx, sy, graphic, frame);
5573   else
5574     DrawGraphic(sx, sy, graphic, frame);
5575 }
5576
5577 static void CheckDynamite(int x, int y)
5578 {
5579   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5580   {
5581     MovDelay[x][y]--;
5582
5583     if (MovDelay[x][y] != 0)
5584     {
5585       DrawDynamite(x, y);
5586       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5587
5588       return;
5589     }
5590   }
5591
5592   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5593
5594   Bang(x, y);
5595 }
5596
5597 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5598 {
5599   boolean num_checked_players = 0;
5600   int i;
5601
5602   for (i = 0; i < MAX_PLAYERS; i++)
5603   {
5604     if (stored_player[i].active)
5605     {
5606       int sx = stored_player[i].jx;
5607       int sy = stored_player[i].jy;
5608
5609       if (num_checked_players == 0)
5610       {
5611         *sx1 = *sx2 = sx;
5612         *sy1 = *sy2 = sy;
5613       }
5614       else
5615       {
5616         *sx1 = MIN(*sx1, sx);
5617         *sy1 = MIN(*sy1, sy);
5618         *sx2 = MAX(*sx2, sx);
5619         *sy2 = MAX(*sy2, sy);
5620       }
5621
5622       num_checked_players++;
5623     }
5624   }
5625 }
5626
5627 static boolean checkIfAllPlayersFitToScreen_RND(void)
5628 {
5629   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5630
5631   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5632
5633   return (sx2 - sx1 < SCR_FIELDX &&
5634           sy2 - sy1 < SCR_FIELDY);
5635 }
5636
5637 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5638 {
5639   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5640
5641   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5642
5643   *sx = (sx1 + sx2) / 2;
5644   *sy = (sy1 + sy2) / 2;
5645 }
5646
5647 static void DrawRelocateScreen(int old_x, int old_y, int x, int y,
5648                                boolean center_screen, boolean quick_relocation)
5649 {
5650   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5651   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5652   boolean no_delay = (tape.warp_forward);
5653   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5654   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5655   int new_scroll_x, new_scroll_y;
5656
5657   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5658   {
5659     // case 1: quick relocation inside visible screen (without scrolling)
5660
5661     RedrawPlayfield();
5662
5663     return;
5664   }
5665
5666   if (!level.shifted_relocation || center_screen)
5667   {
5668     // relocation _with_ centering of screen
5669
5670     new_scroll_x = SCROLL_POSITION_X(x);
5671     new_scroll_y = SCROLL_POSITION_Y(y);
5672   }
5673   else
5674   {
5675     // relocation _without_ centering of screen
5676
5677     // apply distance between old and new player position to scroll position
5678     int shifted_scroll_x = scroll_x + (x - old_x);
5679     int shifted_scroll_y = scroll_y + (y - old_y);
5680
5681     // make sure that shifted scroll position does not scroll beyond screen
5682     new_scroll_x = SCROLL_POSITION_X(shifted_scroll_x + MIDPOSX);
5683     new_scroll_y = SCROLL_POSITION_Y(shifted_scroll_y + MIDPOSY);
5684   }
5685
5686   if (quick_relocation)
5687   {
5688     // case 2: quick relocation (redraw without visible scrolling)
5689
5690     scroll_x = new_scroll_x;
5691     scroll_y = new_scroll_y;
5692
5693     RedrawPlayfield();
5694
5695     return;
5696   }
5697
5698   // case 3: visible relocation (with scrolling to new position)
5699
5700   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5701
5702   SetVideoFrameDelay(wait_delay_value);
5703
5704   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5705   {
5706     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5707     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5708
5709     if (dx == 0 && dy == 0)             // no scrolling needed at all
5710       break;
5711
5712     scroll_x -= dx;
5713     scroll_y -= dy;
5714
5715     // set values for horizontal/vertical screen scrolling (half tile size)
5716     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5717     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5718     int pos_x = dx * TILEX / 2;
5719     int pos_y = dy * TILEY / 2;
5720     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5721     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5722
5723     ScrollLevel(dx, dy);
5724     DrawAllPlayers();
5725
5726     // scroll in two steps of half tile size to make things smoother
5727     BlitScreenToBitmapExt_RND(window, fx, fy);
5728
5729     // scroll second step to align at full tile size
5730     BlitScreenToBitmap(window);
5731   }
5732
5733   DrawAllPlayers();
5734   BackToFront();
5735
5736   SetVideoFrameDelay(frame_delay_value_old);
5737 }
5738
5739 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5740 {
5741   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5742   int player_nr = GET_PLAYER_NR(el_player);
5743   struct PlayerInfo *player = &stored_player[player_nr];
5744   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5745   boolean no_delay = (tape.warp_forward);
5746   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5747   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5748   int old_jx = player->jx;
5749   int old_jy = player->jy;
5750   int old_element = Tile[old_jx][old_jy];
5751   int element = Tile[jx][jy];
5752   boolean player_relocated = (old_jx != jx || old_jy != jy);
5753
5754   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5755   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5756   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5757   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5758   int leave_side_horiz = move_dir_horiz;
5759   int leave_side_vert  = move_dir_vert;
5760   int enter_side = enter_side_horiz | enter_side_vert;
5761   int leave_side = leave_side_horiz | leave_side_vert;
5762
5763   if (player->buried)           // do not reanimate dead player
5764     return;
5765
5766   if (!player_relocated)        // no need to relocate the player
5767     return;
5768
5769   if (IS_PLAYER(jx, jy))        // player already placed at new position
5770   {
5771     RemoveField(jx, jy);        // temporarily remove newly placed player
5772     DrawLevelField(jx, jy);
5773   }
5774
5775   if (player->present)
5776   {
5777     while (player->MovPos)
5778     {
5779       ScrollPlayer(player, SCROLL_GO_ON);
5780       ScrollScreen(NULL, SCROLL_GO_ON);
5781
5782       AdvanceFrameAndPlayerCounters(player->index_nr);
5783
5784       DrawPlayer(player);
5785
5786       BackToFront_WithFrameDelay(wait_delay_value);
5787     }
5788
5789     DrawPlayer(player);         // needed here only to cleanup last field
5790     DrawLevelField(player->jx, player->jy);     // remove player graphic
5791
5792     player->is_moving = FALSE;
5793   }
5794
5795   if (IS_CUSTOM_ELEMENT(old_element))
5796     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5797                                CE_LEFT_BY_PLAYER,
5798                                player->index_bit, leave_side);
5799
5800   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5801                                       CE_PLAYER_LEAVES_X,
5802                                       player->index_bit, leave_side);
5803
5804   Tile[jx][jy] = el_player;
5805   InitPlayerField(jx, jy, el_player, TRUE);
5806
5807   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5808      possible that the relocation target field did not contain a player element,
5809      but a walkable element, to which the new player was relocated -- in this
5810      case, restore that (already initialized!) element on the player field */
5811   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5812   {
5813     Tile[jx][jy] = element;     // restore previously existing element
5814   }
5815
5816   // only visually relocate centered player
5817   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy,
5818                      FALSE, level.instant_relocation);
5819
5820   TestIfPlayerTouchesBadThing(jx, jy);
5821   TestIfPlayerTouchesCustomElement(jx, jy);
5822
5823   if (IS_CUSTOM_ELEMENT(element))
5824     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5825                                player->index_bit, enter_side);
5826
5827   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5828                                       player->index_bit, enter_side);
5829
5830   if (player->is_switching)
5831   {
5832     /* ensure that relocation while still switching an element does not cause
5833        a new element to be treated as also switched directly after relocation
5834        (this is important for teleporter switches that teleport the player to
5835        a place where another teleporter switch is in the same direction, which
5836        would then incorrectly be treated as immediately switched before the
5837        direction key that caused the switch was released) */
5838
5839     player->switch_x += jx - old_jx;
5840     player->switch_y += jy - old_jy;
5841   }
5842 }
5843
5844 static void Explode(int ex, int ey, int phase, int mode)
5845 {
5846   int x, y;
5847   int last_phase;
5848   int border_element;
5849
5850   if (game.explosions_delayed)
5851   {
5852     ExplodeField[ex][ey] = mode;
5853     return;
5854   }
5855
5856   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5857   {
5858     int center_element = Tile[ex][ey];
5859     int ce_value = CustomValue[ex][ey];
5860     int ce_score = element_info[center_element].collect_score;
5861     int artwork_element, explosion_element;     // set these values later
5862
5863     // remove things displayed in background while burning dynamite
5864     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5865       Back[ex][ey] = 0;
5866
5867     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5868     {
5869       // put moving element to center field (and let it explode there)
5870       center_element = MovingOrBlocked2Element(ex, ey);
5871       RemoveMovingField(ex, ey);
5872       Tile[ex][ey] = center_element;
5873     }
5874
5875     // now "center_element" is finally determined -- set related values now
5876     artwork_element = center_element;           // for custom player artwork
5877     explosion_element = center_element;         // for custom player artwork
5878
5879     if (IS_PLAYER(ex, ey))
5880     {
5881       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5882
5883       artwork_element = stored_player[player_nr].artwork_element;
5884
5885       if (level.use_explosion_element[player_nr])
5886       {
5887         explosion_element = level.explosion_element[player_nr];
5888         artwork_element = explosion_element;
5889       }
5890     }
5891
5892     if (mode == EX_TYPE_NORMAL ||
5893         mode == EX_TYPE_CENTER ||
5894         mode == EX_TYPE_CROSS)
5895       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5896
5897     last_phase = element_info[explosion_element].explosion_delay + 1;
5898
5899     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5900     {
5901       int xx = x - ex + 1;
5902       int yy = y - ey + 1;
5903       int element;
5904
5905       if (!IN_LEV_FIELD(x, y) ||
5906           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5907           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5908         continue;
5909
5910       element = Tile[x][y];
5911
5912       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5913       {
5914         element = MovingOrBlocked2Element(x, y);
5915
5916         if (!IS_EXPLOSION_PROOF(element))
5917           RemoveMovingField(x, y);
5918       }
5919
5920       // indestructible elements can only explode in center (but not flames)
5921       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5922                                            mode == EX_TYPE_BORDER)) ||
5923           element == EL_FLAMES)
5924         continue;
5925
5926       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5927          behaviour, for example when touching a yamyam that explodes to rocks
5928          with active deadly shield, a rock is created under the player !!! */
5929       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5930 #if 0
5931       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5932           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5933            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5934 #else
5935       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5936 #endif
5937       {
5938         if (IS_ACTIVE_BOMB(element))
5939         {
5940           // re-activate things under the bomb like gate or penguin
5941           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5942           Back[x][y] = 0;
5943         }
5944
5945         continue;
5946       }
5947
5948       // save walkable background elements while explosion on same tile
5949       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5950           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5951         Back[x][y] = element;
5952
5953       // ignite explodable elements reached by other explosion
5954       if (element == EL_EXPLOSION)
5955         element = Store2[x][y];
5956
5957       if (AmoebaNr[x][y] &&
5958           (element == EL_AMOEBA_FULL ||
5959            element == EL_BD_AMOEBA ||
5960            element == EL_AMOEBA_GROWING))
5961       {
5962         AmoebaCnt[AmoebaNr[x][y]]--;
5963         AmoebaCnt2[AmoebaNr[x][y]]--;
5964       }
5965
5966       RemoveField(x, y);
5967
5968       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5969       {
5970         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5971
5972         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5973
5974         if (PLAYERINFO(ex, ey)->use_murphy)
5975           Store[x][y] = EL_EMPTY;
5976       }
5977
5978       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5979       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5980       else if (IS_PLAYER_ELEMENT(center_element))
5981         Store[x][y] = EL_EMPTY;
5982       else if (center_element == EL_YAMYAM)
5983         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5984       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5985         Store[x][y] = element_info[center_element].content.e[xx][yy];
5986 #if 1
5987       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5988       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5989       // otherwise) -- FIX THIS !!!
5990       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5991         Store[x][y] = element_info[element].content.e[1][1];
5992 #else
5993       else if (!CAN_EXPLODE(element))
5994         Store[x][y] = element_info[element].content.e[1][1];
5995 #endif
5996       else
5997         Store[x][y] = EL_EMPTY;
5998
5999       if (IS_CUSTOM_ELEMENT(center_element))
6000         Store[x][y] = (Store[x][y] == EL_CURRENT_CE_VALUE ? ce_value :
6001                        Store[x][y] == EL_CURRENT_CE_SCORE ? ce_score :
6002                        Store[x][y] >= EL_PREV_CE_8 &&
6003                        Store[x][y] <= EL_NEXT_CE_8 ?
6004                        RESOLVED_REFERENCE_ELEMENT(center_element, Store[x][y]) :
6005                        Store[x][y]);
6006
6007       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6008           center_element == EL_AMOEBA_TO_DIAMOND)
6009         Store2[x][y] = element;
6010
6011       Tile[x][y] = EL_EXPLOSION;
6012       GfxElement[x][y] = artwork_element;
6013
6014       ExplodePhase[x][y] = 1;
6015       ExplodeDelay[x][y] = last_phase;
6016
6017       Stop[x][y] = TRUE;
6018     }
6019
6020     if (center_element == EL_YAMYAM)
6021       game.yamyam_content_nr =
6022         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6023
6024     return;
6025   }
6026
6027   if (Stop[ex][ey])
6028     return;
6029
6030   x = ex;
6031   y = ey;
6032
6033   if (phase == 1)
6034     GfxFrame[x][y] = 0;         // restart explosion animation
6035
6036   last_phase = ExplodeDelay[x][y];
6037
6038   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6039
6040   // this can happen if the player leaves an explosion just in time
6041   if (GfxElement[x][y] == EL_UNDEFINED)
6042     GfxElement[x][y] = EL_EMPTY;
6043
6044   border_element = Store2[x][y];
6045   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6046     border_element = StorePlayer[x][y];
6047
6048   if (phase == element_info[border_element].ignition_delay ||
6049       phase == last_phase)
6050   {
6051     boolean border_explosion = FALSE;
6052
6053     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6054         !PLAYER_EXPLOSION_PROTECTED(x, y))
6055     {
6056       KillPlayerUnlessExplosionProtected(x, y);
6057       border_explosion = TRUE;
6058     }
6059     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6060     {
6061       Tile[x][y] = Store2[x][y];
6062       Store2[x][y] = 0;
6063       Bang(x, y);
6064       border_explosion = TRUE;
6065     }
6066     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6067     {
6068       AmoebaToDiamond(x, y);
6069       Store2[x][y] = 0;
6070       border_explosion = TRUE;
6071     }
6072
6073     // if an element just explodes due to another explosion (chain-reaction),
6074     // do not immediately end the new explosion when it was the last frame of
6075     // the explosion (as it would be done in the following "if"-statement!)
6076     if (border_explosion && phase == last_phase)
6077       return;
6078   }
6079
6080   // this can happen if the player was just killed by an explosion
6081   if (GfxElement[x][y] == EL_UNDEFINED)
6082     GfxElement[x][y] = EL_EMPTY;
6083
6084   if (phase == last_phase)
6085   {
6086     int element;
6087
6088     element = Tile[x][y] = Store[x][y];
6089     Store[x][y] = Store2[x][y] = 0;
6090     GfxElement[x][y] = EL_UNDEFINED;
6091
6092     // player can escape from explosions and might therefore be still alive
6093     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6094         element <= EL_PLAYER_IS_EXPLODING_4)
6095     {
6096       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6097       int explosion_element = EL_PLAYER_1 + player_nr;
6098       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6099       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6100
6101       if (level.use_explosion_element[player_nr])
6102         explosion_element = level.explosion_element[player_nr];
6103
6104       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6105                     element_info[explosion_element].content.e[xx][yy]);
6106     }
6107
6108     // restore probably existing indestructible background element
6109     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6110       element = Tile[x][y] = Back[x][y];
6111     Back[x][y] = 0;
6112
6113     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6114     GfxDir[x][y] = MV_NONE;
6115     ChangeDelay[x][y] = 0;
6116     ChangePage[x][y] = -1;
6117
6118     CustomValue[x][y] = 0;
6119
6120     InitField_WithBug2(x, y, FALSE);
6121
6122     TEST_DrawLevelField(x, y);
6123
6124     TestIfElementTouchesCustomElement(x, y);
6125
6126     if (GFX_CRUMBLED(element))
6127       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6128
6129     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6130       StorePlayer[x][y] = 0;
6131
6132     if (IS_PLAYER_ELEMENT(element))
6133       RelocatePlayer(x, y, element);
6134   }
6135   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6136   {
6137     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6138     int frame = getGraphicAnimationFrameXY(graphic, x, y);
6139
6140     if (phase == 1)
6141       TEST_DrawLevelFieldCrumbled(x, y);
6142
6143     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6144     {
6145       DrawLevelElement(x, y, Back[x][y]);
6146       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6147     }
6148     else if (IS_WALKABLE_UNDER(Back[x][y]))
6149     {
6150       DrawLevelGraphic(x, y, graphic, frame);
6151       DrawLevelElementThruMask(x, y, Back[x][y]);
6152     }
6153     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6154       DrawLevelGraphic(x, y, graphic, frame);
6155   }
6156 }
6157
6158 static void DynaExplode(int ex, int ey)
6159 {
6160   int i, j;
6161   int dynabomb_element = Tile[ex][ey];
6162   int dynabomb_size = 1;
6163   boolean dynabomb_xl = FALSE;
6164   struct PlayerInfo *player;
6165   struct XY *xy = xy_topdown;
6166
6167   if (IS_ACTIVE_BOMB(dynabomb_element))
6168   {
6169     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6170     dynabomb_size = player->dynabomb_size;
6171     dynabomb_xl = player->dynabomb_xl;
6172     player->dynabombs_left++;
6173   }
6174
6175   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6176
6177   for (i = 0; i < NUM_DIRECTIONS; i++)
6178   {
6179     for (j = 1; j <= dynabomb_size; j++)
6180     {
6181       int x = ex + j * xy[i].x;
6182       int y = ey + j * xy[i].y;
6183       int element;
6184
6185       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6186         break;
6187
6188       element = Tile[x][y];
6189
6190       // do not restart explosions of fields with active bombs
6191       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6192         continue;
6193
6194       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6195
6196       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6197           !IS_DIGGABLE(element) && !dynabomb_xl)
6198         break;
6199     }
6200   }
6201 }
6202
6203 void Bang(int x, int y)
6204 {
6205   int element = MovingOrBlocked2Element(x, y);
6206   int explosion_type = EX_TYPE_NORMAL;
6207
6208   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6209   {
6210     struct PlayerInfo *player = PLAYERINFO(x, y);
6211
6212     element = Tile[x][y] = player->initial_element;
6213
6214     if (level.use_explosion_element[player->index_nr])
6215     {
6216       int explosion_element = level.explosion_element[player->index_nr];
6217
6218       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6219         explosion_type = EX_TYPE_CROSS;
6220       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6221         explosion_type = EX_TYPE_CENTER;
6222     }
6223   }
6224
6225   switch (element)
6226   {
6227     case EL_BUG:
6228     case EL_SPACESHIP:
6229     case EL_BD_BUTTERFLY:
6230     case EL_BD_FIREFLY:
6231     case EL_YAMYAM:
6232     case EL_DARK_YAMYAM:
6233     case EL_ROBOT:
6234     case EL_PACMAN:
6235     case EL_MOLE:
6236       RaiseScoreElement(element);
6237       break;
6238
6239     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6240     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6241     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6242     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6243     case EL_DYNABOMB_INCREASE_NUMBER:
6244     case EL_DYNABOMB_INCREASE_SIZE:
6245     case EL_DYNABOMB_INCREASE_POWER:
6246       explosion_type = EX_TYPE_DYNA;
6247       break;
6248
6249     case EL_DC_LANDMINE:
6250       explosion_type = EX_TYPE_CENTER;
6251       break;
6252
6253     case EL_PENGUIN:
6254     case EL_LAMP:
6255     case EL_LAMP_ACTIVE:
6256     case EL_AMOEBA_TO_DIAMOND:
6257       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6258         explosion_type = EX_TYPE_CENTER;
6259       break;
6260
6261     default:
6262       if (element_info[element].explosion_type == EXPLODES_CROSS)
6263         explosion_type = EX_TYPE_CROSS;
6264       else if (element_info[element].explosion_type == EXPLODES_1X1)
6265         explosion_type = EX_TYPE_CENTER;
6266       break;
6267   }
6268
6269   if (explosion_type == EX_TYPE_DYNA)
6270     DynaExplode(x, y);
6271   else
6272     Explode(x, y, EX_PHASE_START, explosion_type);
6273
6274   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6275 }
6276
6277 static void SplashAcid(int x, int y)
6278 {
6279   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6280       (!IN_LEV_FIELD(x - 1, y - 2) ||
6281        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6282     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6283
6284   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6285       (!IN_LEV_FIELD(x + 1, y - 2) ||
6286        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6287     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6288
6289   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6290 }
6291
6292 static void InitBeltMovement(void)
6293 {
6294   static int belt_base_element[4] =
6295   {
6296     EL_CONVEYOR_BELT_1_LEFT,
6297     EL_CONVEYOR_BELT_2_LEFT,
6298     EL_CONVEYOR_BELT_3_LEFT,
6299     EL_CONVEYOR_BELT_4_LEFT
6300   };
6301   static int belt_base_active_element[4] =
6302   {
6303     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6304     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6305     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6306     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6307   };
6308
6309   int x, y, i, j;
6310
6311   // set frame order for belt animation graphic according to belt direction
6312   for (i = 0; i < NUM_BELTS; i++)
6313   {
6314     int belt_nr = i;
6315
6316     for (j = 0; j < NUM_BELT_PARTS; j++)
6317     {
6318       int element = belt_base_active_element[belt_nr] + j;
6319       int graphic_1 = el2img(element);
6320       int graphic_2 = el2panelimg(element);
6321
6322       if (game.belt_dir[i] == MV_LEFT)
6323       {
6324         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6325         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6326       }
6327       else
6328       {
6329         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6330         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6331       }
6332     }
6333   }
6334
6335   SCAN_PLAYFIELD(x, y)
6336   {
6337     int element = Tile[x][y];
6338
6339     for (i = 0; i < NUM_BELTS; i++)
6340     {
6341       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6342       {
6343         int e_belt_nr = getBeltNrFromBeltElement(element);
6344         int belt_nr = i;
6345
6346         if (e_belt_nr == belt_nr)
6347         {
6348           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6349
6350           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6351         }
6352       }
6353     }
6354   }
6355 }
6356
6357 static void ToggleBeltSwitch(int x, int y)
6358 {
6359   static int belt_base_element[4] =
6360   {
6361     EL_CONVEYOR_BELT_1_LEFT,
6362     EL_CONVEYOR_BELT_2_LEFT,
6363     EL_CONVEYOR_BELT_3_LEFT,
6364     EL_CONVEYOR_BELT_4_LEFT
6365   };
6366   static int belt_base_active_element[4] =
6367   {
6368     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6369     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6370     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6371     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6372   };
6373   static int belt_base_switch_element[4] =
6374   {
6375     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6376     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6377     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6378     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6379   };
6380   static int belt_move_dir[4] =
6381   {
6382     MV_LEFT,
6383     MV_NONE,
6384     MV_RIGHT,
6385     MV_NONE,
6386   };
6387
6388   int element = Tile[x][y];
6389   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6390   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6391   int belt_dir = belt_move_dir[belt_dir_nr];
6392   int xx, yy, i;
6393
6394   if (!IS_BELT_SWITCH(element))
6395     return;
6396
6397   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6398   game.belt_dir[belt_nr] = belt_dir;
6399
6400   if (belt_dir_nr == 3)
6401     belt_dir_nr = 1;
6402
6403   // set frame order for belt animation graphic according to belt direction
6404   for (i = 0; i < NUM_BELT_PARTS; i++)
6405   {
6406     int element = belt_base_active_element[belt_nr] + i;
6407     int graphic_1 = el2img(element);
6408     int graphic_2 = el2panelimg(element);
6409
6410     if (belt_dir == MV_LEFT)
6411     {
6412       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6413       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6414     }
6415     else
6416     {
6417       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6418       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6419     }
6420   }
6421
6422   SCAN_PLAYFIELD(xx, yy)
6423   {
6424     int element = Tile[xx][yy];
6425
6426     if (IS_BELT_SWITCH(element))
6427     {
6428       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6429
6430       if (e_belt_nr == belt_nr)
6431       {
6432         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6433         TEST_DrawLevelField(xx, yy);
6434       }
6435     }
6436     else if (IS_BELT(element) && belt_dir != MV_NONE)
6437     {
6438       int e_belt_nr = getBeltNrFromBeltElement(element);
6439
6440       if (e_belt_nr == belt_nr)
6441       {
6442         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6443
6444         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6445         TEST_DrawLevelField(xx, yy);
6446       }
6447     }
6448     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6449     {
6450       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6451
6452       if (e_belt_nr == belt_nr)
6453       {
6454         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6455
6456         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6457         TEST_DrawLevelField(xx, yy);
6458       }
6459     }
6460   }
6461 }
6462
6463 static void ToggleSwitchgateSwitch(void)
6464 {
6465   int xx, yy;
6466
6467   game.switchgate_pos = !game.switchgate_pos;
6468
6469   SCAN_PLAYFIELD(xx, yy)
6470   {
6471     int element = Tile[xx][yy];
6472
6473     if (element == EL_SWITCHGATE_SWITCH_UP)
6474     {
6475       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6476       TEST_DrawLevelField(xx, yy);
6477     }
6478     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6479     {
6480       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6481       TEST_DrawLevelField(xx, yy);
6482     }
6483     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6484     {
6485       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6486       TEST_DrawLevelField(xx, yy);
6487     }
6488     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6489     {
6490       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6491       TEST_DrawLevelField(xx, yy);
6492     }
6493     else if (element == EL_SWITCHGATE_OPEN ||
6494              element == EL_SWITCHGATE_OPENING)
6495     {
6496       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6497
6498       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6499     }
6500     else if (element == EL_SWITCHGATE_CLOSED ||
6501              element == EL_SWITCHGATE_CLOSING)
6502     {
6503       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6504
6505       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6506     }
6507   }
6508 }
6509
6510 static int getInvisibleActiveFromInvisibleElement(int element)
6511 {
6512   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6513           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6514           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6515           element);
6516 }
6517
6518 static int getInvisibleFromInvisibleActiveElement(int element)
6519 {
6520   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6521           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6522           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6523           element);
6524 }
6525
6526 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6527 {
6528   int x, y;
6529
6530   SCAN_PLAYFIELD(x, y)
6531   {
6532     int element = Tile[x][y];
6533
6534     if (element == EL_LIGHT_SWITCH &&
6535         game.light_time_left > 0)
6536     {
6537       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6538       TEST_DrawLevelField(x, y);
6539     }
6540     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6541              game.light_time_left == 0)
6542     {
6543       Tile[x][y] = EL_LIGHT_SWITCH;
6544       TEST_DrawLevelField(x, y);
6545     }
6546     else if (element == EL_EMC_DRIPPER &&
6547              game.light_time_left > 0)
6548     {
6549       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6550       TEST_DrawLevelField(x, y);
6551     }
6552     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6553              game.light_time_left == 0)
6554     {
6555       Tile[x][y] = EL_EMC_DRIPPER;
6556       TEST_DrawLevelField(x, y);
6557     }
6558     else if (element == EL_INVISIBLE_STEELWALL ||
6559              element == EL_INVISIBLE_WALL ||
6560              element == EL_INVISIBLE_SAND)
6561     {
6562       if (game.light_time_left > 0)
6563         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6564
6565       TEST_DrawLevelField(x, y);
6566
6567       // uncrumble neighbour fields, if needed
6568       if (element == EL_INVISIBLE_SAND)
6569         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6570     }
6571     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6572              element == EL_INVISIBLE_WALL_ACTIVE ||
6573              element == EL_INVISIBLE_SAND_ACTIVE)
6574     {
6575       if (game.light_time_left == 0)
6576         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6577
6578       TEST_DrawLevelField(x, y);
6579
6580       // re-crumble neighbour fields, if needed
6581       if (element == EL_INVISIBLE_SAND)
6582         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6583     }
6584   }
6585 }
6586
6587 static void RedrawAllInvisibleElementsForLenses(void)
6588 {
6589   int x, y;
6590
6591   SCAN_PLAYFIELD(x, y)
6592   {
6593     int element = Tile[x][y];
6594
6595     if (element == EL_EMC_DRIPPER &&
6596         game.lenses_time_left > 0)
6597     {
6598       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6599       TEST_DrawLevelField(x, y);
6600     }
6601     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6602              game.lenses_time_left == 0)
6603     {
6604       Tile[x][y] = EL_EMC_DRIPPER;
6605       TEST_DrawLevelField(x, y);
6606     }
6607     else if (element == EL_INVISIBLE_STEELWALL ||
6608              element == EL_INVISIBLE_WALL ||
6609              element == EL_INVISIBLE_SAND)
6610     {
6611       if (game.lenses_time_left > 0)
6612         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6613
6614       TEST_DrawLevelField(x, y);
6615
6616       // uncrumble neighbour fields, if needed
6617       if (element == EL_INVISIBLE_SAND)
6618         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6619     }
6620     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6621              element == EL_INVISIBLE_WALL_ACTIVE ||
6622              element == EL_INVISIBLE_SAND_ACTIVE)
6623     {
6624       if (game.lenses_time_left == 0)
6625         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6626
6627       TEST_DrawLevelField(x, y);
6628
6629       // re-crumble neighbour fields, if needed
6630       if (element == EL_INVISIBLE_SAND)
6631         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6632     }
6633   }
6634 }
6635
6636 static void RedrawAllInvisibleElementsForMagnifier(void)
6637 {
6638   int x, y;
6639
6640   SCAN_PLAYFIELD(x, y)
6641   {
6642     int element = Tile[x][y];
6643
6644     if (element == EL_EMC_FAKE_GRASS &&
6645         game.magnify_time_left > 0)
6646     {
6647       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6648       TEST_DrawLevelField(x, y);
6649     }
6650     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6651              game.magnify_time_left == 0)
6652     {
6653       Tile[x][y] = EL_EMC_FAKE_GRASS;
6654       TEST_DrawLevelField(x, y);
6655     }
6656     else if (IS_GATE_GRAY(element) &&
6657              game.magnify_time_left > 0)
6658     {
6659       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6660                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6661                     IS_EM_GATE_GRAY(element) ?
6662                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6663                     IS_EMC_GATE_GRAY(element) ?
6664                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6665                     IS_DC_GATE_GRAY(element) ?
6666                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6667                     element);
6668       TEST_DrawLevelField(x, y);
6669     }
6670     else if (IS_GATE_GRAY_ACTIVE(element) &&
6671              game.magnify_time_left == 0)
6672     {
6673       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6674                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6675                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6676                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6677                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6678                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6679                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6680                     EL_DC_GATE_WHITE_GRAY :
6681                     element);
6682       TEST_DrawLevelField(x, y);
6683     }
6684   }
6685 }
6686
6687 static void ToggleLightSwitch(int x, int y)
6688 {
6689   int element = Tile[x][y];
6690
6691   game.light_time_left =
6692     (element == EL_LIGHT_SWITCH ?
6693      level.time_light * FRAMES_PER_SECOND : 0);
6694
6695   RedrawAllLightSwitchesAndInvisibleElements();
6696 }
6697
6698 static void ActivateTimegateSwitch(int x, int y)
6699 {
6700   int xx, yy;
6701
6702   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6703
6704   SCAN_PLAYFIELD(xx, yy)
6705   {
6706     int element = Tile[xx][yy];
6707
6708     if (element == EL_TIMEGATE_CLOSED ||
6709         element == EL_TIMEGATE_CLOSING)
6710     {
6711       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6712       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6713     }
6714
6715     /*
6716     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6717     {
6718       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6719       TEST_DrawLevelField(xx, yy);
6720     }
6721     */
6722
6723   }
6724
6725   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6726                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6727 }
6728
6729 static void Impact(int x, int y)
6730 {
6731   boolean last_line = (y == lev_fieldy - 1);
6732   boolean object_hit = FALSE;
6733   boolean impact = (last_line || object_hit);
6734   int element = Tile[x][y];
6735   int smashed = EL_STEELWALL;
6736
6737   if (!last_line)       // check if element below was hit
6738   {
6739     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6740       return;
6741
6742     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6743                                          MovDir[x][y + 1] != MV_DOWN ||
6744                                          MovPos[x][y + 1] <= TILEY / 2));
6745
6746     // do not smash moving elements that left the smashed field in time
6747     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6748         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6749       object_hit = FALSE;
6750
6751 #if USE_QUICKSAND_IMPACT_BUGFIX
6752     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6753     {
6754       RemoveMovingField(x, y + 1);
6755       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6756       Tile[x][y + 2] = EL_ROCK;
6757       TEST_DrawLevelField(x, y + 2);
6758
6759       object_hit = TRUE;
6760     }
6761
6762     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6763     {
6764       RemoveMovingField(x, y + 1);
6765       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6766       Tile[x][y + 2] = EL_ROCK;
6767       TEST_DrawLevelField(x, y + 2);
6768
6769       object_hit = TRUE;
6770     }
6771 #endif
6772
6773     if (object_hit)
6774       smashed = MovingOrBlocked2Element(x, y + 1);
6775
6776     impact = (last_line || object_hit);
6777   }
6778
6779   if (!last_line && smashed == EL_ACID) // element falls into acid
6780   {
6781     SplashAcid(x, y + 1);
6782     return;
6783   }
6784
6785   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6786   // only reset graphic animation if graphic really changes after impact
6787   if (impact &&
6788       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6789   {
6790     ResetGfxAnimation(x, y);
6791     TEST_DrawLevelField(x, y);
6792   }
6793
6794   if (impact && CAN_EXPLODE_IMPACT(element))
6795   {
6796     Bang(x, y);
6797     return;
6798   }
6799   else if (impact && element == EL_PEARL &&
6800            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6801   {
6802     ResetGfxAnimation(x, y);
6803
6804     Tile[x][y] = EL_PEARL_BREAKING;
6805     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6806     return;
6807   }
6808   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6809   {
6810     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6811
6812     return;
6813   }
6814
6815   if (impact && element == EL_AMOEBA_DROP)
6816   {
6817     if (object_hit && IS_PLAYER(x, y + 1))
6818       KillPlayerUnlessEnemyProtected(x, y + 1);
6819     else if (object_hit && smashed == EL_PENGUIN)
6820       Bang(x, y + 1);
6821     else
6822     {
6823       Tile[x][y] = EL_AMOEBA_GROWING;
6824       Store[x][y] = EL_AMOEBA_WET;
6825
6826       ResetRandomAnimationValue(x, y);
6827     }
6828     return;
6829   }
6830
6831   if (object_hit)               // check which object was hit
6832   {
6833     if ((CAN_PASS_MAGIC_WALL(element) && 
6834          (smashed == EL_MAGIC_WALL ||
6835           smashed == EL_BD_MAGIC_WALL)) ||
6836         (CAN_PASS_DC_MAGIC_WALL(element) &&
6837          smashed == EL_DC_MAGIC_WALL))
6838     {
6839       int xx, yy;
6840       int activated_magic_wall =
6841         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6842          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6843          EL_DC_MAGIC_WALL_ACTIVE);
6844
6845       // activate magic wall / mill
6846       SCAN_PLAYFIELD(xx, yy)
6847       {
6848         if (Tile[xx][yy] == smashed)
6849           Tile[xx][yy] = activated_magic_wall;
6850       }
6851
6852       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6853       game.magic_wall_active = TRUE;
6854
6855       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6856                             SND_MAGIC_WALL_ACTIVATING :
6857                             smashed == EL_BD_MAGIC_WALL ?
6858                             SND_BD_MAGIC_WALL_ACTIVATING :
6859                             SND_DC_MAGIC_WALL_ACTIVATING));
6860     }
6861
6862     if (IS_PLAYER(x, y + 1))
6863     {
6864       if (CAN_SMASH_PLAYER(element))
6865       {
6866         KillPlayerUnlessEnemyProtected(x, y + 1);
6867         return;
6868       }
6869     }
6870     else if (smashed == EL_PENGUIN)
6871     {
6872       if (CAN_SMASH_PLAYER(element))
6873       {
6874         Bang(x, y + 1);
6875         return;
6876       }
6877     }
6878     else if (element == EL_BD_DIAMOND)
6879     {
6880       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6881       {
6882         Bang(x, y + 1);
6883         return;
6884       }
6885     }
6886     else if (((element == EL_SP_INFOTRON ||
6887                element == EL_SP_ZONK) &&
6888               (smashed == EL_SP_SNIKSNAK ||
6889                smashed == EL_SP_ELECTRON ||
6890                smashed == EL_SP_DISK_ORANGE)) ||
6891              (element == EL_SP_INFOTRON &&
6892               smashed == EL_SP_DISK_YELLOW))
6893     {
6894       Bang(x, y + 1);
6895       return;
6896     }
6897     else if (CAN_SMASH_EVERYTHING(element))
6898     {
6899       if (IS_CLASSIC_ENEMY(smashed) ||
6900           CAN_EXPLODE_SMASHED(smashed))
6901       {
6902         Bang(x, y + 1);
6903         return;
6904       }
6905       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6906       {
6907         if (smashed == EL_LAMP ||
6908             smashed == EL_LAMP_ACTIVE)
6909         {
6910           Bang(x, y + 1);
6911           return;
6912         }
6913         else if (smashed == EL_NUT)
6914         {
6915           Tile[x][y + 1] = EL_NUT_BREAKING;
6916           PlayLevelSound(x, y, SND_NUT_BREAKING);
6917           RaiseScoreElement(EL_NUT);
6918           return;
6919         }
6920         else if (smashed == EL_PEARL)
6921         {
6922           ResetGfxAnimation(x, y);
6923
6924           Tile[x][y + 1] = EL_PEARL_BREAKING;
6925           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6926           return;
6927         }
6928         else if (smashed == EL_DIAMOND)
6929         {
6930           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6931           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6932           return;
6933         }
6934         else if (IS_BELT_SWITCH(smashed))
6935         {
6936           ToggleBeltSwitch(x, y + 1);
6937         }
6938         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6939                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6940                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6941                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6942         {
6943           ToggleSwitchgateSwitch();
6944         }
6945         else if (smashed == EL_LIGHT_SWITCH ||
6946                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6947         {
6948           ToggleLightSwitch(x, y + 1);
6949         }
6950         else
6951         {
6952           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6953
6954           CheckElementChangeBySide(x, y + 1, smashed, element,
6955                                    CE_SWITCHED, CH_SIDE_TOP);
6956           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6957                                             CH_SIDE_TOP);
6958         }
6959       }
6960       else
6961       {
6962         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6963       }
6964     }
6965   }
6966
6967   // play sound of magic wall / mill
6968   if (!last_line &&
6969       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6970        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6971        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6972   {
6973     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6974       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6975     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6976       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6977     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6978       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6979
6980     return;
6981   }
6982
6983   // play sound of object that hits the ground
6984   if (last_line || object_hit)
6985     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6986 }
6987
6988 static void TurnRoundExt(int x, int y)
6989 {
6990   static struct
6991   {
6992     int dx, dy;
6993   } move_xy[] =
6994   {
6995     {  0,  0 },
6996     { -1,  0 },
6997     { +1,  0 },
6998     {  0,  0 },
6999     {  0, -1 },
7000     {  0,  0 }, { 0, 0 }, { 0, 0 },
7001     {  0, +1 }
7002   };
7003   static struct
7004   {
7005     int left, right, back;
7006   } turn[] =
7007   {
7008     { 0,        0,              0        },
7009     { MV_DOWN,  MV_UP,          MV_RIGHT },
7010     { MV_UP,    MV_DOWN,        MV_LEFT  },
7011     { 0,        0,              0        },
7012     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
7013     { 0,        0,              0        },
7014     { 0,        0,              0        },
7015     { 0,        0,              0        },
7016     { MV_RIGHT, MV_LEFT,        MV_UP    }
7017   };
7018
7019   int element = Tile[x][y];
7020   int move_pattern = element_info[element].move_pattern;
7021
7022   int old_move_dir = MovDir[x][y];
7023   int left_dir  = turn[old_move_dir].left;
7024   int right_dir = turn[old_move_dir].right;
7025   int back_dir  = turn[old_move_dir].back;
7026
7027   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7028   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7029   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7030   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7031
7032   int left_x  = x + left_dx,  left_y  = y + left_dy;
7033   int right_x = x + right_dx, right_y = y + right_dy;
7034   int move_x  = x + move_dx,  move_y  = y + move_dy;
7035
7036   int xx, yy;
7037
7038   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7039   {
7040     TestIfBadThingTouchesOtherBadThing(x, y);
7041
7042     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7043       MovDir[x][y] = right_dir;
7044     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7045       MovDir[x][y] = left_dir;
7046
7047     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7048       MovDelay[x][y] = 9;
7049     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
7050       MovDelay[x][y] = 1;
7051   }
7052   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7053   {
7054     TestIfBadThingTouchesOtherBadThing(x, y);
7055
7056     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7057       MovDir[x][y] = left_dir;
7058     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7059       MovDir[x][y] = right_dir;
7060
7061     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7062       MovDelay[x][y] = 9;
7063     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
7064       MovDelay[x][y] = 1;
7065   }
7066   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7067   {
7068     TestIfBadThingTouchesOtherBadThing(x, y);
7069
7070     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7071       MovDir[x][y] = left_dir;
7072     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7073       MovDir[x][y] = right_dir;
7074
7075     if (MovDir[x][y] != old_move_dir)
7076       MovDelay[x][y] = 9;
7077   }
7078   else if (element == EL_YAMYAM)
7079   {
7080     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7081     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7082
7083     if (can_turn_left && can_turn_right)
7084       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7085     else if (can_turn_left)
7086       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7087     else if (can_turn_right)
7088       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7089     else
7090       MovDir[x][y] = back_dir;
7091
7092     MovDelay[x][y] = 16 + 16 * RND(3);
7093   }
7094   else if (element == EL_DARK_YAMYAM)
7095   {
7096     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7097                                                          left_x, left_y);
7098     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7099                                                          right_x, right_y);
7100
7101     if (can_turn_left && can_turn_right)
7102       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7103     else if (can_turn_left)
7104       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7105     else if (can_turn_right)
7106       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7107     else
7108       MovDir[x][y] = back_dir;
7109
7110     MovDelay[x][y] = 16 + 16 * RND(3);
7111   }
7112   else if (element == EL_PACMAN)
7113   {
7114     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7115     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7116
7117     if (can_turn_left && can_turn_right)
7118       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7119     else if (can_turn_left)
7120       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7121     else if (can_turn_right)
7122       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7123     else
7124       MovDir[x][y] = back_dir;
7125
7126     MovDelay[x][y] = 6 + RND(40);
7127   }
7128   else if (element == EL_PIG)
7129   {
7130     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7131     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7132     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7133     boolean should_turn_left, should_turn_right, should_move_on;
7134     int rnd_value = 24;
7135     int rnd = RND(rnd_value);
7136
7137     should_turn_left = (can_turn_left &&
7138                         (!can_move_on ||
7139                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7140                                                    y + back_dy + left_dy)));
7141     should_turn_right = (can_turn_right &&
7142                          (!can_move_on ||
7143                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7144                                                     y + back_dy + right_dy)));
7145     should_move_on = (can_move_on &&
7146                       (!can_turn_left ||
7147                        !can_turn_right ||
7148                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7149                                                  y + move_dy + left_dy) ||
7150                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7151                                                  y + move_dy + right_dy)));
7152
7153     if (should_turn_left || should_turn_right || should_move_on)
7154     {
7155       if (should_turn_left && should_turn_right && should_move_on)
7156         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7157                         rnd < 2 * rnd_value / 3 ? right_dir :
7158                         old_move_dir);
7159       else if (should_turn_left && should_turn_right)
7160         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7161       else if (should_turn_left && should_move_on)
7162         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7163       else if (should_turn_right && should_move_on)
7164         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7165       else if (should_turn_left)
7166         MovDir[x][y] = left_dir;
7167       else if (should_turn_right)
7168         MovDir[x][y] = right_dir;
7169       else if (should_move_on)
7170         MovDir[x][y] = old_move_dir;
7171     }
7172     else if (can_move_on && rnd > rnd_value / 8)
7173       MovDir[x][y] = old_move_dir;
7174     else if (can_turn_left && can_turn_right)
7175       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7176     else if (can_turn_left && rnd > rnd_value / 8)
7177       MovDir[x][y] = left_dir;
7178     else if (can_turn_right && rnd > rnd_value/8)
7179       MovDir[x][y] = right_dir;
7180     else
7181       MovDir[x][y] = back_dir;
7182
7183     xx = x + move_xy[MovDir[x][y]].dx;
7184     yy = y + move_xy[MovDir[x][y]].dy;
7185
7186     if (!IN_LEV_FIELD(xx, yy) ||
7187         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7188       MovDir[x][y] = old_move_dir;
7189
7190     MovDelay[x][y] = 0;
7191   }
7192   else if (element == EL_DRAGON)
7193   {
7194     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7195     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7196     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7197     int rnd_value = 24;
7198     int rnd = RND(rnd_value);
7199
7200     if (can_move_on && rnd > rnd_value / 8)
7201       MovDir[x][y] = old_move_dir;
7202     else if (can_turn_left && can_turn_right)
7203       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7204     else if (can_turn_left && rnd > rnd_value / 8)
7205       MovDir[x][y] = left_dir;
7206     else if (can_turn_right && rnd > rnd_value / 8)
7207       MovDir[x][y] = right_dir;
7208     else
7209       MovDir[x][y] = back_dir;
7210
7211     xx = x + move_xy[MovDir[x][y]].dx;
7212     yy = y + move_xy[MovDir[x][y]].dy;
7213
7214     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7215       MovDir[x][y] = old_move_dir;
7216
7217     MovDelay[x][y] = 0;
7218   }
7219   else if (element == EL_MOLE)
7220   {
7221     boolean can_move_on =
7222       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7223                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7224                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7225     if (!can_move_on)
7226     {
7227       boolean can_turn_left =
7228         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7229                               IS_AMOEBOID(Tile[left_x][left_y])));
7230
7231       boolean can_turn_right =
7232         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7233                               IS_AMOEBOID(Tile[right_x][right_y])));
7234
7235       if (can_turn_left && can_turn_right)
7236         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7237       else if (can_turn_left)
7238         MovDir[x][y] = left_dir;
7239       else
7240         MovDir[x][y] = right_dir;
7241     }
7242
7243     if (MovDir[x][y] != old_move_dir)
7244       MovDelay[x][y] = 9;
7245   }
7246   else if (element == EL_BALLOON)
7247   {
7248     MovDir[x][y] = game.wind_direction;
7249     MovDelay[x][y] = 0;
7250   }
7251   else if (element == EL_SPRING)
7252   {
7253     if (MovDir[x][y] & MV_HORIZONTAL)
7254     {
7255       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7256           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7257       {
7258         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7259         ResetGfxAnimation(move_x, move_y);
7260         TEST_DrawLevelField(move_x, move_y);
7261
7262         MovDir[x][y] = back_dir;
7263       }
7264       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7265                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7266         MovDir[x][y] = MV_NONE;
7267     }
7268
7269     MovDelay[x][y] = 0;
7270   }
7271   else if (element == EL_ROBOT ||
7272            element == EL_SATELLITE ||
7273            element == EL_PENGUIN ||
7274            element == EL_EMC_ANDROID)
7275   {
7276     int attr_x = -1, attr_y = -1;
7277
7278     if (game.all_players_gone)
7279     {
7280       attr_x = game.exit_x;
7281       attr_y = game.exit_y;
7282     }
7283     else
7284     {
7285       int i;
7286
7287       for (i = 0; i < MAX_PLAYERS; i++)
7288       {
7289         struct PlayerInfo *player = &stored_player[i];
7290         int jx = player->jx, jy = player->jy;
7291
7292         if (!player->active)
7293           continue;
7294
7295         if (attr_x == -1 ||
7296             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7297         {
7298           attr_x = jx;
7299           attr_y = jy;
7300         }
7301       }
7302     }
7303
7304     if (element == EL_ROBOT &&
7305         game.robot_wheel_x >= 0 &&
7306         game.robot_wheel_y >= 0 &&
7307         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7308          game.engine_version < VERSION_IDENT(3,1,0,0)))
7309     {
7310       attr_x = game.robot_wheel_x;
7311       attr_y = game.robot_wheel_y;
7312     }
7313
7314     if (element == EL_PENGUIN)
7315     {
7316       int i;
7317       struct XY *xy = xy_topdown;
7318
7319       for (i = 0; i < NUM_DIRECTIONS; i++)
7320       {
7321         int ex = x + xy[i].x;
7322         int ey = y + xy[i].y;
7323
7324         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7325                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7326                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7327                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7328         {
7329           attr_x = ex;
7330           attr_y = ey;
7331           break;
7332         }
7333       }
7334     }
7335
7336     MovDir[x][y] = MV_NONE;
7337     if (attr_x < x)
7338       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7339     else if (attr_x > x)
7340       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7341     if (attr_y < y)
7342       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7343     else if (attr_y > y)
7344       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7345
7346     if (element == EL_ROBOT)
7347     {
7348       int newx, newy;
7349
7350       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7351         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7352       Moving2Blocked(x, y, &newx, &newy);
7353
7354       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7355         MovDelay[x][y] = 8 + 8 * !RND(3);
7356       else
7357         MovDelay[x][y] = 16;
7358     }
7359     else if (element == EL_PENGUIN)
7360     {
7361       int newx, newy;
7362
7363       MovDelay[x][y] = 1;
7364
7365       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7366       {
7367         boolean first_horiz = RND(2);
7368         int new_move_dir = MovDir[x][y];
7369
7370         MovDir[x][y] =
7371           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7372         Moving2Blocked(x, y, &newx, &newy);
7373
7374         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7375           return;
7376
7377         MovDir[x][y] =
7378           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7379         Moving2Blocked(x, y, &newx, &newy);
7380
7381         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7382           return;
7383
7384         MovDir[x][y] = old_move_dir;
7385         return;
7386       }
7387     }
7388     else if (element == EL_SATELLITE)
7389     {
7390       int newx, newy;
7391
7392       MovDelay[x][y] = 1;
7393
7394       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7395       {
7396         boolean first_horiz = RND(2);
7397         int new_move_dir = MovDir[x][y];
7398
7399         MovDir[x][y] =
7400           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7401         Moving2Blocked(x, y, &newx, &newy);
7402
7403         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7404           return;
7405
7406         MovDir[x][y] =
7407           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7408         Moving2Blocked(x, y, &newx, &newy);
7409
7410         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7411           return;
7412
7413         MovDir[x][y] = old_move_dir;
7414         return;
7415       }
7416     }
7417     else if (element == EL_EMC_ANDROID)
7418     {
7419       static int check_pos[16] =
7420       {
7421         -1,             //  0 => (invalid)
7422         7,              //  1 => MV_LEFT
7423         3,              //  2 => MV_RIGHT
7424         -1,             //  3 => (invalid)
7425         1,              //  4 =>            MV_UP
7426         0,              //  5 => MV_LEFT  | MV_UP
7427         2,              //  6 => MV_RIGHT | MV_UP
7428         -1,             //  7 => (invalid)
7429         5,              //  8 =>            MV_DOWN
7430         6,              //  9 => MV_LEFT  | MV_DOWN
7431         4,              // 10 => MV_RIGHT | MV_DOWN
7432         -1,             // 11 => (invalid)
7433         -1,             // 12 => (invalid)
7434         -1,             // 13 => (invalid)
7435         -1,             // 14 => (invalid)
7436         -1,             // 15 => (invalid)
7437       };
7438       static struct
7439       {
7440         int dx, dy;
7441         int dir;
7442       } check_xy[8] =
7443       {
7444         { -1, -1,       MV_LEFT  | MV_UP   },
7445         {  0, -1,                  MV_UP   },
7446         { +1, -1,       MV_RIGHT | MV_UP   },
7447         { +1,  0,       MV_RIGHT           },
7448         { +1, +1,       MV_RIGHT | MV_DOWN },
7449         {  0, +1,                  MV_DOWN },
7450         { -1, +1,       MV_LEFT  | MV_DOWN },
7451         { -1,  0,       MV_LEFT            },
7452       };
7453       int start_pos, check_order;
7454       boolean can_clone = FALSE;
7455       int i;
7456
7457       // check if there is any free field around current position
7458       for (i = 0; i < 8; i++)
7459       {
7460         int newx = x + check_xy[i].dx;
7461         int newy = y + check_xy[i].dy;
7462
7463         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7464         {
7465           can_clone = TRUE;
7466
7467           break;
7468         }
7469       }
7470
7471       if (can_clone)            // randomly find an element to clone
7472       {
7473         can_clone = FALSE;
7474
7475         start_pos = check_pos[RND(8)];
7476         check_order = (RND(2) ? -1 : +1);
7477
7478         for (i = 0; i < 8; i++)
7479         {
7480           int pos_raw = start_pos + i * check_order;
7481           int pos = (pos_raw + 8) % 8;
7482           int newx = x + check_xy[pos].dx;
7483           int newy = y + check_xy[pos].dy;
7484
7485           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7486           {
7487             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7488             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7489
7490             Store[x][y] = Tile[newx][newy];
7491
7492             can_clone = TRUE;
7493
7494             break;
7495           }
7496         }
7497       }
7498
7499       if (can_clone)            // randomly find a direction to move
7500       {
7501         can_clone = FALSE;
7502
7503         start_pos = check_pos[RND(8)];
7504         check_order = (RND(2) ? -1 : +1);
7505
7506         for (i = 0; i < 8; i++)
7507         {
7508           int pos_raw = start_pos + i * check_order;
7509           int pos = (pos_raw + 8) % 8;
7510           int newx = x + check_xy[pos].dx;
7511           int newy = y + check_xy[pos].dy;
7512           int new_move_dir = check_xy[pos].dir;
7513
7514           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7515           {
7516             MovDir[x][y] = new_move_dir;
7517             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7518
7519             can_clone = TRUE;
7520
7521             break;
7522           }
7523         }
7524       }
7525
7526       if (can_clone)            // cloning and moving successful
7527         return;
7528
7529       // cannot clone -- try to move towards player
7530
7531       start_pos = check_pos[MovDir[x][y] & 0x0f];
7532       check_order = (RND(2) ? -1 : +1);
7533
7534       for (i = 0; i < 3; i++)
7535       {
7536         // first check start_pos, then previous/next or (next/previous) pos
7537         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7538         int pos = (pos_raw + 8) % 8;
7539         int newx = x + check_xy[pos].dx;
7540         int newy = y + check_xy[pos].dy;
7541         int new_move_dir = check_xy[pos].dir;
7542
7543         if (IS_PLAYER(newx, newy))
7544           break;
7545
7546         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7547         {
7548           MovDir[x][y] = new_move_dir;
7549           MovDelay[x][y] = level.android_move_time * 8 + 1;
7550
7551           break;
7552         }
7553       }
7554     }
7555   }
7556   else if (move_pattern == MV_TURNING_LEFT ||
7557            move_pattern == MV_TURNING_RIGHT ||
7558            move_pattern == MV_TURNING_LEFT_RIGHT ||
7559            move_pattern == MV_TURNING_RIGHT_LEFT ||
7560            move_pattern == MV_TURNING_RANDOM ||
7561            move_pattern == MV_ALL_DIRECTIONS)
7562   {
7563     boolean can_turn_left =
7564       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7565     boolean can_turn_right =
7566       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y);
7567
7568     if (element_info[element].move_stepsize == 0)       // "not moving"
7569       return;
7570
7571     if (move_pattern == MV_TURNING_LEFT)
7572       MovDir[x][y] = left_dir;
7573     else if (move_pattern == MV_TURNING_RIGHT)
7574       MovDir[x][y] = right_dir;
7575     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7576       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7577     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7578       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7579     else if (move_pattern == MV_TURNING_RANDOM)
7580       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7581                       can_turn_right && !can_turn_left ? right_dir :
7582                       RND(2) ? left_dir : right_dir);
7583     else if (can_turn_left && can_turn_right)
7584       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7585     else if (can_turn_left)
7586       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7587     else if (can_turn_right)
7588       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7589     else
7590       MovDir[x][y] = back_dir;
7591
7592     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7593   }
7594   else if (move_pattern == MV_HORIZONTAL ||
7595            move_pattern == MV_VERTICAL)
7596   {
7597     if (move_pattern & old_move_dir)
7598       MovDir[x][y] = back_dir;
7599     else if (move_pattern == MV_HORIZONTAL)
7600       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7601     else if (move_pattern == MV_VERTICAL)
7602       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7603
7604     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7605   }
7606   else if (move_pattern & MV_ANY_DIRECTION)
7607   {
7608     MovDir[x][y] = move_pattern;
7609     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7610   }
7611   else if (move_pattern & MV_WIND_DIRECTION)
7612   {
7613     MovDir[x][y] = game.wind_direction;
7614     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7615   }
7616   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7617   {
7618     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7619       MovDir[x][y] = left_dir;
7620     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7621       MovDir[x][y] = right_dir;
7622
7623     if (MovDir[x][y] != old_move_dir)
7624       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7625   }
7626   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7627   {
7628     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7629       MovDir[x][y] = right_dir;
7630     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7631       MovDir[x][y] = left_dir;
7632
7633     if (MovDir[x][y] != old_move_dir)
7634       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7635   }
7636   else if (move_pattern == MV_TOWARDS_PLAYER ||
7637            move_pattern == MV_AWAY_FROM_PLAYER)
7638   {
7639     int attr_x = -1, attr_y = -1;
7640     int newx, newy;
7641     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7642
7643     if (game.all_players_gone)
7644     {
7645       attr_x = game.exit_x;
7646       attr_y = game.exit_y;
7647     }
7648     else
7649     {
7650       int i;
7651
7652       for (i = 0; i < MAX_PLAYERS; i++)
7653       {
7654         struct PlayerInfo *player = &stored_player[i];
7655         int jx = player->jx, jy = player->jy;
7656
7657         if (!player->active)
7658           continue;
7659
7660         if (attr_x == -1 ||
7661             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7662         {
7663           attr_x = jx;
7664           attr_y = jy;
7665         }
7666       }
7667     }
7668
7669     MovDir[x][y] = MV_NONE;
7670     if (attr_x < x)
7671       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7672     else if (attr_x > x)
7673       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7674     if (attr_y < y)
7675       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7676     else if (attr_y > y)
7677       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7678
7679     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7680
7681     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7682     {
7683       boolean first_horiz = RND(2);
7684       int new_move_dir = MovDir[x][y];
7685
7686       if (element_info[element].move_stepsize == 0)     // "not moving"
7687       {
7688         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7689         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7690
7691         return;
7692       }
7693
7694       MovDir[x][y] =
7695         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7696       Moving2Blocked(x, y, &newx, &newy);
7697
7698       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7699         return;
7700
7701       MovDir[x][y] =
7702         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7703       Moving2Blocked(x, y, &newx, &newy);
7704
7705       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7706         return;
7707
7708       MovDir[x][y] = old_move_dir;
7709     }
7710   }
7711   else if (move_pattern == MV_WHEN_PUSHED ||
7712            move_pattern == MV_WHEN_DROPPED)
7713   {
7714     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7715       MovDir[x][y] = MV_NONE;
7716
7717     MovDelay[x][y] = 0;
7718   }
7719   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7720   {
7721     struct XY *test_xy = xy_topdown;
7722     static int test_dir[4] =
7723     {
7724       MV_UP,
7725       MV_LEFT,
7726       MV_RIGHT,
7727       MV_DOWN
7728     };
7729     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7730     int move_preference = -1000000;     // start with very low preference
7731     int new_move_dir = MV_NONE;
7732     int start_test = RND(4);
7733     int i;
7734
7735     for (i = 0; i < NUM_DIRECTIONS; i++)
7736     {
7737       int j = (start_test + i) % 4;
7738       int move_dir = test_dir[j];
7739       int move_dir_preference;
7740
7741       xx = x + test_xy[j].x;
7742       yy = y + test_xy[j].y;
7743
7744       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7745           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7746       {
7747         new_move_dir = move_dir;
7748
7749         break;
7750       }
7751
7752       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7753         continue;
7754
7755       move_dir_preference = -1 * RunnerVisit[xx][yy];
7756       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7757         move_dir_preference = PlayerVisit[xx][yy];
7758
7759       if (move_dir_preference > move_preference)
7760       {
7761         // prefer field that has not been visited for the longest time
7762         move_preference = move_dir_preference;
7763         new_move_dir = move_dir;
7764       }
7765       else if (move_dir_preference == move_preference &&
7766                move_dir == old_move_dir)
7767       {
7768         // prefer last direction when all directions are preferred equally
7769         move_preference = move_dir_preference;
7770         new_move_dir = move_dir;
7771       }
7772     }
7773
7774     MovDir[x][y] = new_move_dir;
7775     if (old_move_dir != new_move_dir)
7776       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7777   }
7778 }
7779
7780 static void TurnRound(int x, int y)
7781 {
7782   int direction = MovDir[x][y];
7783
7784   TurnRoundExt(x, y);
7785
7786   GfxDir[x][y] = MovDir[x][y];
7787
7788   if (direction != MovDir[x][y])
7789     GfxFrame[x][y] = 0;
7790
7791   if (MovDelay[x][y])
7792     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7793
7794   ResetGfxFrame(x, y);
7795 }
7796
7797 static boolean JustBeingPushed(int x, int y)
7798 {
7799   int i;
7800
7801   for (i = 0; i < MAX_PLAYERS; i++)
7802   {
7803     struct PlayerInfo *player = &stored_player[i];
7804
7805     if (player->active && player->is_pushing && player->MovPos)
7806     {
7807       int next_jx = player->jx + (player->jx - player->last_jx);
7808       int next_jy = player->jy + (player->jy - player->last_jy);
7809
7810       if (x == next_jx && y == next_jy)
7811         return TRUE;
7812     }
7813   }
7814
7815   return FALSE;
7816 }
7817
7818 static void StartMoving(int x, int y)
7819 {
7820   boolean started_moving = FALSE;       // some elements can fall _and_ move
7821   int element = Tile[x][y];
7822
7823   if (Stop[x][y])
7824     return;
7825
7826   if (MovDelay[x][y] == 0)
7827     GfxAction[x][y] = ACTION_DEFAULT;
7828
7829   if (CAN_FALL(element) && y < lev_fieldy - 1)
7830   {
7831     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7832         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7833       if (JustBeingPushed(x, y))
7834         return;
7835
7836     if (element == EL_QUICKSAND_FULL)
7837     {
7838       if (IS_FREE(x, y + 1))
7839       {
7840         InitMovingField(x, y, MV_DOWN);
7841         started_moving = TRUE;
7842
7843         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7844 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7845         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7846           Store[x][y] = EL_ROCK;
7847 #else
7848         Store[x][y] = EL_ROCK;
7849 #endif
7850
7851         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7852       }
7853       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7854       {
7855         if (!MovDelay[x][y])
7856         {
7857           MovDelay[x][y] = TILEY + 1;
7858
7859           ResetGfxAnimation(x, y);
7860           ResetGfxAnimation(x, y + 1);
7861         }
7862
7863         if (MovDelay[x][y])
7864         {
7865           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7866           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7867
7868           MovDelay[x][y]--;
7869           if (MovDelay[x][y])
7870             return;
7871         }
7872
7873         Tile[x][y] = EL_QUICKSAND_EMPTY;
7874         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7875         Store[x][y + 1] = Store[x][y];
7876         Store[x][y] = 0;
7877
7878         PlayLevelSoundAction(x, y, ACTION_FILLING);
7879       }
7880       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7881       {
7882         if (!MovDelay[x][y])
7883         {
7884           MovDelay[x][y] = TILEY + 1;
7885
7886           ResetGfxAnimation(x, y);
7887           ResetGfxAnimation(x, y + 1);
7888         }
7889
7890         if (MovDelay[x][y])
7891         {
7892           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7893           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7894
7895           MovDelay[x][y]--;
7896           if (MovDelay[x][y])
7897             return;
7898         }
7899
7900         Tile[x][y] = EL_QUICKSAND_EMPTY;
7901         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7902         Store[x][y + 1] = Store[x][y];
7903         Store[x][y] = 0;
7904
7905         PlayLevelSoundAction(x, y, ACTION_FILLING);
7906       }
7907     }
7908     else if (element == EL_QUICKSAND_FAST_FULL)
7909     {
7910       if (IS_FREE(x, y + 1))
7911       {
7912         InitMovingField(x, y, MV_DOWN);
7913         started_moving = TRUE;
7914
7915         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7916 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7917         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7918           Store[x][y] = EL_ROCK;
7919 #else
7920         Store[x][y] = EL_ROCK;
7921 #endif
7922
7923         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7924       }
7925       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7926       {
7927         if (!MovDelay[x][y])
7928         {
7929           MovDelay[x][y] = TILEY + 1;
7930
7931           ResetGfxAnimation(x, y);
7932           ResetGfxAnimation(x, y + 1);
7933         }
7934
7935         if (MovDelay[x][y])
7936         {
7937           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7938           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7939
7940           MovDelay[x][y]--;
7941           if (MovDelay[x][y])
7942             return;
7943         }
7944
7945         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7946         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7947         Store[x][y + 1] = Store[x][y];
7948         Store[x][y] = 0;
7949
7950         PlayLevelSoundAction(x, y, ACTION_FILLING);
7951       }
7952       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7953       {
7954         if (!MovDelay[x][y])
7955         {
7956           MovDelay[x][y] = TILEY + 1;
7957
7958           ResetGfxAnimation(x, y);
7959           ResetGfxAnimation(x, y + 1);
7960         }
7961
7962         if (MovDelay[x][y])
7963         {
7964           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7965           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7966
7967           MovDelay[x][y]--;
7968           if (MovDelay[x][y])
7969             return;
7970         }
7971
7972         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7973         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7974         Store[x][y + 1] = Store[x][y];
7975         Store[x][y] = 0;
7976
7977         PlayLevelSoundAction(x, y, ACTION_FILLING);
7978       }
7979     }
7980     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7981              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7982     {
7983       InitMovingField(x, y, MV_DOWN);
7984       started_moving = TRUE;
7985
7986       Tile[x][y] = EL_QUICKSAND_FILLING;
7987       Store[x][y] = element;
7988
7989       PlayLevelSoundAction(x, y, ACTION_FILLING);
7990     }
7991     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7992              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7993     {
7994       InitMovingField(x, y, MV_DOWN);
7995       started_moving = TRUE;
7996
7997       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7998       Store[x][y] = element;
7999
8000       PlayLevelSoundAction(x, y, ACTION_FILLING);
8001     }
8002     else if (element == EL_MAGIC_WALL_FULL)
8003     {
8004       if (IS_FREE(x, y + 1))
8005       {
8006         InitMovingField(x, y, MV_DOWN);
8007         started_moving = TRUE;
8008
8009         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
8010         Store[x][y] = EL_CHANGED(Store[x][y]);
8011       }
8012       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8013       {
8014         if (!MovDelay[x][y])
8015           MovDelay[x][y] = TILEY / 4 + 1;
8016
8017         if (MovDelay[x][y])
8018         {
8019           MovDelay[x][y]--;
8020           if (MovDelay[x][y])
8021             return;
8022         }
8023
8024         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
8025         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
8026         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8027         Store[x][y] = 0;
8028       }
8029     }
8030     else if (element == EL_BD_MAGIC_WALL_FULL)
8031     {
8032       if (IS_FREE(x, y + 1))
8033       {
8034         InitMovingField(x, y, MV_DOWN);
8035         started_moving = TRUE;
8036
8037         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8038         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8039       }
8040       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8041       {
8042         if (!MovDelay[x][y])
8043           MovDelay[x][y] = TILEY / 4 + 1;
8044
8045         if (MovDelay[x][y])
8046         {
8047           MovDelay[x][y]--;
8048           if (MovDelay[x][y])
8049             return;
8050         }
8051
8052         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8053         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8054         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8055         Store[x][y] = 0;
8056       }
8057     }
8058     else if (element == EL_DC_MAGIC_WALL_FULL)
8059     {
8060       if (IS_FREE(x, y + 1))
8061       {
8062         InitMovingField(x, y, MV_DOWN);
8063         started_moving = TRUE;
8064
8065         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8066         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8067       }
8068       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8069       {
8070         if (!MovDelay[x][y])
8071           MovDelay[x][y] = TILEY / 4 + 1;
8072
8073         if (MovDelay[x][y])
8074         {
8075           MovDelay[x][y]--;
8076           if (MovDelay[x][y])
8077             return;
8078         }
8079
8080         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8081         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8082         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8083         Store[x][y] = 0;
8084       }
8085     }
8086     else if ((CAN_PASS_MAGIC_WALL(element) &&
8087               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8088                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8089              (CAN_PASS_DC_MAGIC_WALL(element) &&
8090               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8091
8092     {
8093       InitMovingField(x, y, MV_DOWN);
8094       started_moving = TRUE;
8095
8096       Tile[x][y] =
8097         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8098          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8099          EL_DC_MAGIC_WALL_FILLING);
8100       Store[x][y] = element;
8101     }
8102     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
8103     {
8104       SplashAcid(x, y + 1);
8105
8106       InitMovingField(x, y, MV_DOWN);
8107       started_moving = TRUE;
8108
8109       Store[x][y] = EL_ACID;
8110     }
8111     else if (
8112              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8113               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8114              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8115               CAN_FALL(element) && WasJustFalling[x][y] &&
8116               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8117
8118              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8119               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8120               (Tile[x][y + 1] == EL_BLOCKED)))
8121     {
8122       /* this is needed for a special case not covered by calling "Impact()"
8123          from "ContinueMoving()": if an element moves to a tile directly below
8124          another element which was just falling on that tile (which was empty
8125          in the previous frame), the falling element above would just stop
8126          instead of smashing the element below (in previous version, the above
8127          element was just checked for "moving" instead of "falling", resulting
8128          in incorrect smashes caused by horizontal movement of the above
8129          element; also, the case of the player being the element to smash was
8130          simply not covered here... :-/ ) */
8131
8132       CheckCollision[x][y] = 0;
8133       CheckImpact[x][y] = 0;
8134
8135       Impact(x, y);
8136     }
8137     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8138     {
8139       if (MovDir[x][y] == MV_NONE)
8140       {
8141         InitMovingField(x, y, MV_DOWN);
8142         started_moving = TRUE;
8143       }
8144     }
8145     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8146     {
8147       if (WasJustFalling[x][y]) // prevent animation from being restarted
8148         MovDir[x][y] = MV_DOWN;
8149
8150       InitMovingField(x, y, MV_DOWN);
8151       started_moving = TRUE;
8152     }
8153     else if (element == EL_AMOEBA_DROP)
8154     {
8155       Tile[x][y] = EL_AMOEBA_GROWING;
8156       Store[x][y] = EL_AMOEBA_WET;
8157     }
8158     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8159               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8160              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8161              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8162     {
8163       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8164                                 (IS_FREE(x - 1, y + 1) ||
8165                                  Tile[x - 1][y + 1] == EL_ACID));
8166       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8167                                 (IS_FREE(x + 1, y + 1) ||
8168                                  Tile[x + 1][y + 1] == EL_ACID));
8169       boolean can_fall_any  = (can_fall_left || can_fall_right);
8170       boolean can_fall_both = (can_fall_left && can_fall_right);
8171       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8172
8173       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8174       {
8175         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8176           can_fall_right = FALSE;
8177         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8178           can_fall_left = FALSE;
8179         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8180           can_fall_right = FALSE;
8181         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8182           can_fall_left = FALSE;
8183
8184         can_fall_any  = (can_fall_left || can_fall_right);
8185         can_fall_both = FALSE;
8186       }
8187
8188       if (can_fall_both)
8189       {
8190         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8191           can_fall_right = FALSE;       // slip down on left side
8192         else
8193           can_fall_left = !(can_fall_right = RND(2));
8194
8195         can_fall_both = FALSE;
8196       }
8197
8198       if (can_fall_any)
8199       {
8200         // if not determined otherwise, prefer left side for slipping down
8201         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8202         started_moving = TRUE;
8203       }
8204     }
8205     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8206     {
8207       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8208       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8209       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8210       int belt_dir = game.belt_dir[belt_nr];
8211
8212       if ((belt_dir == MV_LEFT  && left_is_free) ||
8213           (belt_dir == MV_RIGHT && right_is_free))
8214       {
8215         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8216
8217         InitMovingField(x, y, belt_dir);
8218         started_moving = TRUE;
8219
8220         Pushed[x][y] = TRUE;
8221         Pushed[nextx][y] = TRUE;
8222
8223         GfxAction[x][y] = ACTION_DEFAULT;
8224       }
8225       else
8226       {
8227         MovDir[x][y] = 0;       // if element was moving, stop it
8228       }
8229     }
8230   }
8231
8232   // not "else if" because of elements that can fall and move (EL_SPRING)
8233   if (CAN_MOVE(element) && !started_moving)
8234   {
8235     int move_pattern = element_info[element].move_pattern;
8236     int newx, newy;
8237
8238     Moving2Blocked(x, y, &newx, &newy);
8239
8240     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8241       return;
8242
8243     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8244         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8245     {
8246       WasJustMoving[x][y] = 0;
8247       CheckCollision[x][y] = 0;
8248
8249       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8250
8251       if (Tile[x][y] != element)        // element has changed
8252         return;
8253     }
8254
8255     if (!MovDelay[x][y])        // start new movement phase
8256     {
8257       // all objects that can change their move direction after each step
8258       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8259
8260       if (element != EL_YAMYAM &&
8261           element != EL_DARK_YAMYAM &&
8262           element != EL_PACMAN &&
8263           !(move_pattern & MV_ANY_DIRECTION) &&
8264           move_pattern != MV_TURNING_LEFT &&
8265           move_pattern != MV_TURNING_RIGHT &&
8266           move_pattern != MV_TURNING_LEFT_RIGHT &&
8267           move_pattern != MV_TURNING_RIGHT_LEFT &&
8268           move_pattern != MV_TURNING_RANDOM)
8269       {
8270         TurnRound(x, y);
8271
8272         if (MovDelay[x][y] && (element == EL_BUG ||
8273                                element == EL_SPACESHIP ||
8274                                element == EL_SP_SNIKSNAK ||
8275                                element == EL_SP_ELECTRON ||
8276                                element == EL_MOLE))
8277           TEST_DrawLevelField(x, y);
8278       }
8279     }
8280
8281     if (MovDelay[x][y])         // wait some time before next movement
8282     {
8283       MovDelay[x][y]--;
8284
8285       if (element == EL_ROBOT ||
8286           element == EL_YAMYAM ||
8287           element == EL_DARK_YAMYAM)
8288       {
8289         DrawLevelElementAnimationIfNeeded(x, y, element);
8290         PlayLevelSoundAction(x, y, ACTION_WAITING);
8291       }
8292       else if (element == EL_SP_ELECTRON)
8293         DrawLevelElementAnimationIfNeeded(x, y, element);
8294       else if (element == EL_DRAGON)
8295       {
8296         int i;
8297         int dir = MovDir[x][y];
8298         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8299         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8300         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8301                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8302                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8303                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8304         int frame = getGraphicAnimationFrameXY(graphic, x, y);
8305
8306         GfxAction[x][y] = ACTION_ATTACKING;
8307
8308         if (IS_PLAYER(x, y))
8309           DrawPlayerField(x, y);
8310         else
8311           TEST_DrawLevelField(x, y);
8312
8313         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8314
8315         for (i = 1; i <= 3; i++)
8316         {
8317           int xx = x + i * dx;
8318           int yy = y + i * dy;
8319           int sx = SCREENX(xx);
8320           int sy = SCREENY(yy);
8321           int flame_graphic = graphic + (i - 1);
8322
8323           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8324             break;
8325
8326           if (MovDelay[x][y])
8327           {
8328             int flamed = MovingOrBlocked2Element(xx, yy);
8329
8330             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8331               Bang(xx, yy);
8332             else
8333               RemoveMovingField(xx, yy);
8334
8335             ChangeDelay[xx][yy] = 0;
8336
8337             Tile[xx][yy] = EL_FLAMES;
8338
8339             if (IN_SCR_FIELD(sx, sy))
8340             {
8341               TEST_DrawLevelFieldCrumbled(xx, yy);
8342               DrawScreenGraphic(sx, sy, flame_graphic, frame);
8343             }
8344           }
8345           else
8346           {
8347             if (Tile[xx][yy] == EL_FLAMES)
8348               Tile[xx][yy] = EL_EMPTY;
8349             TEST_DrawLevelField(xx, yy);
8350           }
8351         }
8352       }
8353
8354       if (MovDelay[x][y])       // element still has to wait some time
8355       {
8356         PlayLevelSoundAction(x, y, ACTION_WAITING);
8357
8358         return;
8359       }
8360     }
8361
8362     // now make next step
8363
8364     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8365
8366     if (DONT_COLLIDE_WITH(element) &&
8367         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8368         !PLAYER_ENEMY_PROTECTED(newx, newy))
8369     {
8370       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8371
8372       return;
8373     }
8374
8375     else if (CAN_MOVE_INTO_ACID(element) &&
8376              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8377              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8378              (MovDir[x][y] == MV_DOWN ||
8379               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8380     {
8381       SplashAcid(newx, newy);
8382       Store[x][y] = EL_ACID;
8383     }
8384     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8385     {
8386       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8387           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8388           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8389           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8390       {
8391         RemoveField(x, y);
8392         TEST_DrawLevelField(x, y);
8393
8394         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8395         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8396           DrawGraphicThruMask(SCREENX(newx), SCREENY(newy), el2img(element), 0);
8397
8398         game.friends_still_needed--;
8399         if (!game.friends_still_needed &&
8400             !game.GameOver &&
8401             game.all_players_gone)
8402           LevelSolved();
8403
8404         return;
8405       }
8406       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8407       {
8408         if (DigField(local_player, x, y, newx, newy, 0, 0, DF_DIG) == MP_MOVING)
8409           TEST_DrawLevelField(newx, newy);
8410         else
8411           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8412       }
8413       else if (!IS_FREE(newx, newy))
8414       {
8415         GfxAction[x][y] = ACTION_WAITING;
8416
8417         if (IS_PLAYER(x, y))
8418           DrawPlayerField(x, y);
8419         else
8420           TEST_DrawLevelField(x, y);
8421
8422         return;
8423       }
8424     }
8425     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8426     {
8427       if (IS_FOOD_PIG(Tile[newx][newy]))
8428       {
8429         if (IS_MOVING(newx, newy))
8430           RemoveMovingField(newx, newy);
8431         else
8432         {
8433           Tile[newx][newy] = EL_EMPTY;
8434           TEST_DrawLevelField(newx, newy);
8435         }
8436
8437         PlayLevelSound(x, y, SND_PIG_DIGGING);
8438       }
8439       else if (!IS_FREE(newx, newy))
8440       {
8441         if (IS_PLAYER(x, y))
8442           DrawPlayerField(x, y);
8443         else
8444           TEST_DrawLevelField(x, y);
8445
8446         return;
8447       }
8448     }
8449     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8450     {
8451       if (Store[x][y] != EL_EMPTY)
8452       {
8453         boolean can_clone = FALSE;
8454         int xx, yy;
8455
8456         // check if element to clone is still there
8457         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8458         {
8459           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8460           {
8461             can_clone = TRUE;
8462
8463             break;
8464           }
8465         }
8466
8467         // cannot clone or target field not free anymore -- do not clone
8468         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8469           Store[x][y] = EL_EMPTY;
8470       }
8471
8472       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8473       {
8474         if (IS_MV_DIAGONAL(MovDir[x][y]))
8475         {
8476           int diagonal_move_dir = MovDir[x][y];
8477           int stored = Store[x][y];
8478           int change_delay = 8;
8479           int graphic;
8480
8481           // android is moving diagonally
8482
8483           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8484
8485           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8486           GfxElement[x][y] = EL_EMC_ANDROID;
8487           GfxAction[x][y] = ACTION_SHRINKING;
8488           GfxDir[x][y] = diagonal_move_dir;
8489           ChangeDelay[x][y] = change_delay;
8490
8491           if (Store[x][y] == EL_EMPTY)
8492             Store[x][y] = GfxElementEmpty[x][y];
8493
8494           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8495                                    GfxDir[x][y]);
8496
8497           DrawLevelGraphicAnimation(x, y, graphic);
8498           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8499
8500           if (Tile[newx][newy] == EL_ACID)
8501           {
8502             SplashAcid(newx, newy);
8503
8504             return;
8505           }
8506
8507           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8508
8509           Store[newx][newy] = EL_EMC_ANDROID;
8510           GfxElement[newx][newy] = EL_EMC_ANDROID;
8511           GfxAction[newx][newy] = ACTION_GROWING;
8512           GfxDir[newx][newy] = diagonal_move_dir;
8513           ChangeDelay[newx][newy] = change_delay;
8514
8515           graphic = el_act_dir2img(GfxElement[newx][newy],
8516                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8517
8518           DrawLevelGraphicAnimation(newx, newy, graphic);
8519           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8520
8521           return;
8522         }
8523         else
8524         {
8525           Tile[newx][newy] = EL_EMPTY;
8526           TEST_DrawLevelField(newx, newy);
8527
8528           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8529         }
8530       }
8531       else if (!IS_FREE(newx, newy))
8532       {
8533         return;
8534       }
8535     }
8536     else if (IS_CUSTOM_ELEMENT(element) &&
8537              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8538     {
8539       if (!DigFieldByCE(newx, newy, element))
8540         return;
8541
8542       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8543       {
8544         RunnerVisit[x][y] = FrameCounter;
8545         PlayerVisit[x][y] /= 8;         // expire player visit path
8546       }
8547     }
8548     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8549     {
8550       if (!IS_FREE(newx, newy))
8551       {
8552         if (IS_PLAYER(x, y))
8553           DrawPlayerField(x, y);
8554         else
8555           TEST_DrawLevelField(x, y);
8556
8557         return;
8558       }
8559       else
8560       {
8561         boolean wanna_flame = !RND(10);
8562         int dx = newx - x, dy = newy - y;
8563         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8564         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8565         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8566                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8567         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8568                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8569
8570         if ((wanna_flame ||
8571              IS_CLASSIC_ENEMY(element1) ||
8572              IS_CLASSIC_ENEMY(element2)) &&
8573             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8574             element1 != EL_FLAMES && element2 != EL_FLAMES)
8575         {
8576           ResetGfxAnimation(x, y);
8577           GfxAction[x][y] = ACTION_ATTACKING;
8578
8579           if (IS_PLAYER(x, y))
8580             DrawPlayerField(x, y);
8581           else
8582             TEST_DrawLevelField(x, y);
8583
8584           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8585
8586           MovDelay[x][y] = 50;
8587
8588           Tile[newx][newy] = EL_FLAMES;
8589           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8590             Tile[newx1][newy1] = EL_FLAMES;
8591           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8592             Tile[newx2][newy2] = EL_FLAMES;
8593
8594           return;
8595         }
8596       }
8597     }
8598     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8599              Tile[newx][newy] == EL_DIAMOND)
8600     {
8601       if (IS_MOVING(newx, newy))
8602         RemoveMovingField(newx, newy);
8603       else
8604       {
8605         Tile[newx][newy] = EL_EMPTY;
8606         TEST_DrawLevelField(newx, newy);
8607       }
8608
8609       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8610     }
8611     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8612              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8613     {
8614       if (AmoebaNr[newx][newy])
8615       {
8616         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8617         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8618             Tile[newx][newy] == EL_BD_AMOEBA)
8619           AmoebaCnt[AmoebaNr[newx][newy]]--;
8620       }
8621
8622       if (IS_MOVING(newx, newy))
8623       {
8624         RemoveMovingField(newx, newy);
8625       }
8626       else
8627       {
8628         Tile[newx][newy] = EL_EMPTY;
8629         TEST_DrawLevelField(newx, newy);
8630       }
8631
8632       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8633     }
8634     else if ((element == EL_PACMAN || element == EL_MOLE)
8635              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8636     {
8637       if (AmoebaNr[newx][newy])
8638       {
8639         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8640         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8641             Tile[newx][newy] == EL_BD_AMOEBA)
8642           AmoebaCnt[AmoebaNr[newx][newy]]--;
8643       }
8644
8645       if (element == EL_MOLE)
8646       {
8647         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8648         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8649
8650         ResetGfxAnimation(x, y);
8651         GfxAction[x][y] = ACTION_DIGGING;
8652         TEST_DrawLevelField(x, y);
8653
8654         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8655
8656         return;                         // wait for shrinking amoeba
8657       }
8658       else      // element == EL_PACMAN
8659       {
8660         Tile[newx][newy] = EL_EMPTY;
8661         TEST_DrawLevelField(newx, newy);
8662         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8663       }
8664     }
8665     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8666              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8667               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8668     {
8669       // wait for shrinking amoeba to completely disappear
8670       return;
8671     }
8672     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8673     {
8674       // object was running against a wall
8675
8676       TurnRound(x, y);
8677
8678       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8679         DrawLevelElementAnimation(x, y, element);
8680
8681       if (DONT_TOUCH(element))
8682         TestIfBadThingTouchesPlayer(x, y);
8683
8684       return;
8685     }
8686
8687     InitMovingField(x, y, MovDir[x][y]);
8688
8689     PlayLevelSoundAction(x, y, ACTION_MOVING);
8690   }
8691
8692   if (MovDir[x][y])
8693     ContinueMoving(x, y);
8694 }
8695
8696 void ContinueMoving(int x, int y)
8697 {
8698   int element = Tile[x][y];
8699   struct ElementInfo *ei = &element_info[element];
8700   int direction = MovDir[x][y];
8701   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8702   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8703   int newx = x + dx, newy = y + dy;
8704   int stored = Store[x][y];
8705   int stored_new = Store[newx][newy];
8706   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8707   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8708   boolean last_line = (newy == lev_fieldy - 1);
8709   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8710
8711   if (pushed_by_player)         // special case: moving object pushed by player
8712   {
8713     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x, y)->MovPos));
8714   }
8715   else if (use_step_delay)      // special case: moving object has step delay
8716   {
8717     if (!MovDelay[x][y])
8718       MovPos[x][y] += getElementMoveStepsize(x, y);
8719
8720     if (MovDelay[x][y])
8721       MovDelay[x][y]--;
8722     else
8723       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8724
8725     if (MovDelay[x][y])
8726     {
8727       TEST_DrawLevelField(x, y);
8728
8729       return;   // element is still waiting
8730     }
8731   }
8732   else                          // normal case: generically moving object
8733   {
8734     MovPos[x][y] += getElementMoveStepsize(x, y);
8735   }
8736
8737   if (ABS(MovPos[x][y]) < TILEX)
8738   {
8739     TEST_DrawLevelField(x, y);
8740
8741     return;     // element is still moving
8742   }
8743
8744   // element reached destination field
8745
8746   Tile[x][y] = EL_EMPTY;
8747   Tile[newx][newy] = element;
8748   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8749
8750   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8751   {
8752     element = Tile[newx][newy] = EL_ACID;
8753   }
8754   else if (element == EL_MOLE)
8755   {
8756     Tile[x][y] = EL_SAND;
8757
8758     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8759   }
8760   else if (element == EL_QUICKSAND_FILLING)
8761   {
8762     element = Tile[newx][newy] = get_next_element(element);
8763     Store[newx][newy] = Store[x][y];
8764   }
8765   else if (element == EL_QUICKSAND_EMPTYING)
8766   {
8767     Tile[x][y] = get_next_element(element);
8768     element = Tile[newx][newy] = Store[x][y];
8769   }
8770   else if (element == EL_QUICKSAND_FAST_FILLING)
8771   {
8772     element = Tile[newx][newy] = get_next_element(element);
8773     Store[newx][newy] = Store[x][y];
8774   }
8775   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8776   {
8777     Tile[x][y] = get_next_element(element);
8778     element = Tile[newx][newy] = Store[x][y];
8779   }
8780   else if (element == EL_MAGIC_WALL_FILLING)
8781   {
8782     element = Tile[newx][newy] = get_next_element(element);
8783     if (!game.magic_wall_active)
8784       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8785     Store[newx][newy] = Store[x][y];
8786   }
8787   else if (element == EL_MAGIC_WALL_EMPTYING)
8788   {
8789     Tile[x][y] = get_next_element(element);
8790     if (!game.magic_wall_active)
8791       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8792     element = Tile[newx][newy] = Store[x][y];
8793
8794     InitField(newx, newy, FALSE);
8795   }
8796   else if (element == EL_BD_MAGIC_WALL_FILLING)
8797   {
8798     element = Tile[newx][newy] = get_next_element(element);
8799     if (!game.magic_wall_active)
8800       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8801     Store[newx][newy] = Store[x][y];
8802   }
8803   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8804   {
8805     Tile[x][y] = get_next_element(element);
8806     if (!game.magic_wall_active)
8807       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8808     element = Tile[newx][newy] = Store[x][y];
8809
8810     InitField(newx, newy, FALSE);
8811   }
8812   else if (element == EL_DC_MAGIC_WALL_FILLING)
8813   {
8814     element = Tile[newx][newy] = get_next_element(element);
8815     if (!game.magic_wall_active)
8816       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8817     Store[newx][newy] = Store[x][y];
8818   }
8819   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8820   {
8821     Tile[x][y] = get_next_element(element);
8822     if (!game.magic_wall_active)
8823       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8824     element = Tile[newx][newy] = Store[x][y];
8825
8826     InitField(newx, newy, FALSE);
8827   }
8828   else if (element == EL_AMOEBA_DROPPING)
8829   {
8830     Tile[x][y] = get_next_element(element);
8831     element = Tile[newx][newy] = Store[x][y];
8832   }
8833   else if (element == EL_SOKOBAN_OBJECT)
8834   {
8835     if (Back[x][y])
8836       Tile[x][y] = Back[x][y];
8837
8838     if (Back[newx][newy])
8839       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8840
8841     Back[x][y] = Back[newx][newy] = 0;
8842   }
8843
8844   Store[x][y] = EL_EMPTY;
8845   MovPos[x][y] = 0;
8846   MovDir[x][y] = 0;
8847   MovDelay[x][y] = 0;
8848
8849   MovDelay[newx][newy] = 0;
8850
8851   if (CAN_CHANGE_OR_HAS_ACTION(element))
8852   {
8853     // copy element change control values to new field
8854     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8855     ChangePage[newx][newy]  = ChangePage[x][y];
8856     ChangeCount[newx][newy] = ChangeCount[x][y];
8857     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8858   }
8859
8860   CustomValue[newx][newy] = CustomValue[x][y];
8861
8862   ChangeDelay[x][y] = 0;
8863   ChangePage[x][y] = -1;
8864   ChangeCount[x][y] = 0;
8865   ChangeEvent[x][y] = -1;
8866
8867   CustomValue[x][y] = 0;
8868
8869   // copy animation control values to new field
8870   GfxFrame[newx][newy]  = GfxFrame[x][y];
8871   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8872   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8873   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8874
8875   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8876
8877   // some elements can leave other elements behind after moving
8878   if (ei->move_leave_element != EL_EMPTY &&
8879       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8880       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8881   {
8882     int move_leave_element = ei->move_leave_element;
8883
8884     // this makes it possible to leave the removed element again
8885     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8886       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8887
8888     Tile[x][y] = move_leave_element;
8889
8890     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8891       MovDir[x][y] = direction;
8892
8893     InitField(x, y, FALSE);
8894
8895     if (GFX_CRUMBLED(Tile[x][y]))
8896       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8897
8898     if (IS_PLAYER_ELEMENT(move_leave_element))
8899       RelocatePlayer(x, y, move_leave_element);
8900   }
8901
8902   // do this after checking for left-behind element
8903   ResetGfxAnimation(x, y);      // reset animation values for old field
8904
8905   if (!CAN_MOVE(element) ||
8906       (CAN_FALL(element) && direction == MV_DOWN &&
8907        (element == EL_SPRING ||
8908         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8909         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8910     GfxDir[x][y] = MovDir[newx][newy] = 0;
8911
8912   TEST_DrawLevelField(x, y);
8913   TEST_DrawLevelField(newx, newy);
8914
8915   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8916
8917   // prevent pushed element from moving on in pushed direction
8918   if (pushed_by_player && CAN_MOVE(element) &&
8919       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8920       !(element_info[element].move_pattern & direction))
8921     TurnRound(newx, newy);
8922
8923   // prevent elements on conveyor belt from moving on in last direction
8924   if (pushed_by_conveyor && CAN_FALL(element) &&
8925       direction & MV_HORIZONTAL)
8926     MovDir[newx][newy] = 0;
8927
8928   if (!pushed_by_player)
8929   {
8930     int nextx = newx + dx, nexty = newy + dy;
8931     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8932
8933     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8934
8935     if (CAN_FALL(element) && direction == MV_DOWN)
8936       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8937
8938     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8939       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8940
8941     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8942       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8943   }
8944
8945   if (DONT_TOUCH(element))      // object may be nasty to player or others
8946   {
8947     TestIfBadThingTouchesPlayer(newx, newy);
8948     TestIfBadThingTouchesFriend(newx, newy);
8949
8950     if (!IS_CUSTOM_ELEMENT(element))
8951       TestIfBadThingTouchesOtherBadThing(newx, newy);
8952   }
8953   else if (element == EL_PENGUIN)
8954     TestIfFriendTouchesBadThing(newx, newy);
8955
8956   if (DONT_GET_HIT_BY(element))
8957   {
8958     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8959   }
8960
8961   // give the player one last chance (one more frame) to move away
8962   if (CAN_FALL(element) && direction == MV_DOWN &&
8963       (last_line || (!IS_FREE(x, newy + 1) &&
8964                      (!IS_PLAYER(x, newy + 1) ||
8965                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8966     Impact(x, newy);
8967
8968   if (pushed_by_player && !game.use_change_when_pushing_bug)
8969   {
8970     int push_side = MV_DIR_OPPOSITE(direction);
8971     struct PlayerInfo *player = PLAYERINFO(x, y);
8972
8973     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8974                                player->index_bit, push_side);
8975     CheckTriggeredElementChangeByPlayer(newx, newy, element, CE_PLAYER_PUSHES_X,
8976                                         player->index_bit, push_side);
8977   }
8978
8979   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8980     MovDelay[newx][newy] = 1;
8981
8982   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8983
8984   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8985   TestIfElementHitsCustomElement(newx, newy, direction);
8986   TestIfPlayerTouchesCustomElement(newx, newy);
8987   TestIfElementTouchesCustomElement(newx, newy);
8988
8989   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8990       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8991     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8992                              MV_DIR_OPPOSITE(direction));
8993 }
8994
8995 int AmoebaNeighbourNr(int ax, int ay)
8996 {
8997   int i;
8998   int element = Tile[ax][ay];
8999   int group_nr = 0;
9000   struct XY *xy = xy_topdown;
9001
9002   for (i = 0; i < NUM_DIRECTIONS; i++)
9003   {
9004     int x = ax + xy[i].x;
9005     int y = ay + xy[i].y;
9006
9007     if (!IN_LEV_FIELD(x, y))
9008       continue;
9009
9010     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
9011       group_nr = AmoebaNr[x][y];
9012   }
9013
9014   return group_nr;
9015 }
9016
9017 static void AmoebaMerge(int ax, int ay)
9018 {
9019   int i, x, y, xx, yy;
9020   int new_group_nr = AmoebaNr[ax][ay];
9021   struct XY *xy = xy_topdown;
9022
9023   if (new_group_nr == 0)
9024     return;
9025
9026   for (i = 0; i < NUM_DIRECTIONS; i++)
9027   {
9028     x = ax + xy[i].x;
9029     y = ay + xy[i].y;
9030
9031     if (!IN_LEV_FIELD(x, y))
9032       continue;
9033
9034     if ((Tile[x][y] == EL_AMOEBA_FULL ||
9035          Tile[x][y] == EL_BD_AMOEBA ||
9036          Tile[x][y] == EL_AMOEBA_DEAD) &&
9037         AmoebaNr[x][y] != new_group_nr)
9038     {
9039       int old_group_nr = AmoebaNr[x][y];
9040
9041       if (old_group_nr == 0)
9042         return;
9043
9044       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9045       AmoebaCnt[old_group_nr] = 0;
9046       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9047       AmoebaCnt2[old_group_nr] = 0;
9048
9049       SCAN_PLAYFIELD(xx, yy)
9050       {
9051         if (AmoebaNr[xx][yy] == old_group_nr)
9052           AmoebaNr[xx][yy] = new_group_nr;
9053       }
9054     }
9055   }
9056 }
9057
9058 void AmoebaToDiamond(int ax, int ay)
9059 {
9060   int i, x, y;
9061
9062   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
9063   {
9064     int group_nr = AmoebaNr[ax][ay];
9065
9066 #ifdef DEBUG
9067     if (group_nr == 0)
9068     {
9069       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
9070       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
9071
9072       return;
9073     }
9074 #endif
9075
9076     SCAN_PLAYFIELD(x, y)
9077     {
9078       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9079       {
9080         AmoebaNr[x][y] = 0;
9081         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
9082       }
9083     }
9084
9085     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9086                             SND_AMOEBA_TURNING_TO_GEM :
9087                             SND_AMOEBA_TURNING_TO_ROCK));
9088     Bang(ax, ay);
9089   }
9090   else
9091   {
9092     struct XY *xy = xy_topdown;
9093
9094     for (i = 0; i < NUM_DIRECTIONS; i++)
9095     {
9096       x = ax + xy[i].x;
9097       y = ay + xy[i].y;
9098
9099       if (!IN_LEV_FIELD(x, y))
9100         continue;
9101
9102       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9103       {
9104         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9105                               SND_AMOEBA_TURNING_TO_GEM :
9106                               SND_AMOEBA_TURNING_TO_ROCK));
9107         Bang(x, y);
9108       }
9109     }
9110   }
9111 }
9112
9113 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9114 {
9115   int x, y;
9116   int group_nr = AmoebaNr[ax][ay];
9117   boolean done = FALSE;
9118
9119 #ifdef DEBUG
9120   if (group_nr == 0)
9121   {
9122     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9123     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9124
9125     return;
9126   }
9127 #endif
9128
9129   SCAN_PLAYFIELD(x, y)
9130   {
9131     if (AmoebaNr[x][y] == group_nr &&
9132         (Tile[x][y] == EL_AMOEBA_DEAD ||
9133          Tile[x][y] == EL_BD_AMOEBA ||
9134          Tile[x][y] == EL_AMOEBA_GROWING))
9135     {
9136       AmoebaNr[x][y] = 0;
9137       Tile[x][y] = new_element;
9138       InitField(x, y, FALSE);
9139       TEST_DrawLevelField(x, y);
9140       done = TRUE;
9141     }
9142   }
9143
9144   if (done)
9145     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9146                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9147                             SND_BD_AMOEBA_TURNING_TO_GEM));
9148 }
9149
9150 static void AmoebaGrowing(int x, int y)
9151 {
9152   static DelayCounter sound_delay = { 0 };
9153
9154   if (!MovDelay[x][y])          // start new growing cycle
9155   {
9156     MovDelay[x][y] = 7;
9157
9158     if (DelayReached(&sound_delay))
9159     {
9160       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9161       sound_delay.value = 30;
9162     }
9163   }
9164
9165   if (MovDelay[x][y])           // wait some time before growing bigger
9166   {
9167     MovDelay[x][y]--;
9168     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9169     {
9170       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9171                                            6 - MovDelay[x][y]);
9172
9173       DrawLevelGraphic(x, y, IMG_AMOEBA_GROWING, frame);
9174     }
9175
9176     if (!MovDelay[x][y])
9177     {
9178       Tile[x][y] = Store[x][y];
9179       Store[x][y] = 0;
9180       TEST_DrawLevelField(x, y);
9181     }
9182   }
9183 }
9184
9185 static void AmoebaShrinking(int x, int y)
9186 {
9187   static DelayCounter sound_delay = { 0 };
9188
9189   if (!MovDelay[x][y])          // start new shrinking cycle
9190   {
9191     MovDelay[x][y] = 7;
9192
9193     if (DelayReached(&sound_delay))
9194       sound_delay.value = 30;
9195   }
9196
9197   if (MovDelay[x][y])           // wait some time before shrinking
9198   {
9199     MovDelay[x][y]--;
9200     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9201     {
9202       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9203                                            6 - MovDelay[x][y]);
9204
9205       DrawLevelGraphic(x, y, IMG_AMOEBA_SHRINKING, frame);
9206     }
9207
9208     if (!MovDelay[x][y])
9209     {
9210       Tile[x][y] = EL_EMPTY;
9211       TEST_DrawLevelField(x, y);
9212
9213       // don't let mole enter this field in this cycle;
9214       // (give priority to objects falling to this field from above)
9215       Stop[x][y] = TRUE;
9216     }
9217   }
9218 }
9219
9220 static void AmoebaReproduce(int ax, int ay)
9221 {
9222   int i;
9223   int element = Tile[ax][ay];
9224   int graphic = el2img(element);
9225   int newax = ax, neway = ay;
9226   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9227   struct XY *xy = xy_topdown;
9228
9229   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9230   {
9231     Tile[ax][ay] = EL_AMOEBA_DEAD;
9232     TEST_DrawLevelField(ax, ay);
9233     return;
9234   }
9235
9236   if (IS_ANIMATED(graphic))
9237     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9238
9239   if (!MovDelay[ax][ay])        // start making new amoeba field
9240     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9241
9242   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9243   {
9244     MovDelay[ax][ay]--;
9245     if (MovDelay[ax][ay])
9246       return;
9247   }
9248
9249   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9250   {
9251     int start = RND(4);
9252     int x = ax + xy[start].x;
9253     int y = ay + xy[start].y;
9254
9255     if (!IN_LEV_FIELD(x, y))
9256       return;
9257
9258     if (IS_FREE(x, y) ||
9259         CAN_GROW_INTO(Tile[x][y]) ||
9260         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9261         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9262     {
9263       newax = x;
9264       neway = y;
9265     }
9266
9267     if (newax == ax && neway == ay)
9268       return;
9269   }
9270   else                          // normal or "filled" (BD style) amoeba
9271   {
9272     int start = RND(4);
9273     boolean waiting_for_player = FALSE;
9274
9275     for (i = 0; i < NUM_DIRECTIONS; i++)
9276     {
9277       int j = (start + i) % 4;
9278       int x = ax + xy[j].x;
9279       int y = ay + xy[j].y;
9280
9281       if (!IN_LEV_FIELD(x, y))
9282         continue;
9283
9284       if (IS_FREE(x, y) ||
9285           CAN_GROW_INTO(Tile[x][y]) ||
9286           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9287           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9288       {
9289         newax = x;
9290         neway = y;
9291         break;
9292       }
9293       else if (IS_PLAYER(x, y))
9294         waiting_for_player = TRUE;
9295     }
9296
9297     if (newax == ax && neway == ay)             // amoeba cannot grow
9298     {
9299       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9300       {
9301         Tile[ax][ay] = EL_AMOEBA_DEAD;
9302         TEST_DrawLevelField(ax, ay);
9303         AmoebaCnt[AmoebaNr[ax][ay]]--;
9304
9305         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9306         {
9307           if (element == EL_AMOEBA_FULL)
9308             AmoebaToDiamond(ax, ay);
9309           else if (element == EL_BD_AMOEBA)
9310             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9311         }
9312       }
9313       return;
9314     }
9315     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9316     {
9317       // amoeba gets larger by growing in some direction
9318
9319       int new_group_nr = AmoebaNr[ax][ay];
9320
9321 #ifdef DEBUG
9322   if (new_group_nr == 0)
9323   {
9324     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9325           newax, neway);
9326     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9327
9328     return;
9329   }
9330 #endif
9331
9332       AmoebaNr[newax][neway] = new_group_nr;
9333       AmoebaCnt[new_group_nr]++;
9334       AmoebaCnt2[new_group_nr]++;
9335
9336       // if amoeba touches other amoeba(s) after growing, unify them
9337       AmoebaMerge(newax, neway);
9338
9339       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9340       {
9341         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9342         return;
9343       }
9344     }
9345   }
9346
9347   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9348       (neway == lev_fieldy - 1 && newax != ax))
9349   {
9350     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9351     Store[newax][neway] = element;
9352   }
9353   else if (neway == ay || element == EL_EMC_DRIPPER)
9354   {
9355     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9356
9357     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9358   }
9359   else
9360   {
9361     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9362     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9363     Store[ax][ay] = EL_AMOEBA_DROP;
9364     ContinueMoving(ax, ay);
9365     return;
9366   }
9367
9368   TEST_DrawLevelField(newax, neway);
9369 }
9370
9371 static void Life(int ax, int ay)
9372 {
9373   int x1, y1, x2, y2;
9374   int life_time = 40;
9375   int element = Tile[ax][ay];
9376   int graphic = el2img(element);
9377   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9378                          level.biomaze);
9379   boolean changed = FALSE;
9380
9381   if (IS_ANIMATED(graphic))
9382     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9383
9384   if (Stop[ax][ay])
9385     return;
9386
9387   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9388     MovDelay[ax][ay] = life_time;
9389
9390   if (MovDelay[ax][ay])         // wait some time before next cycle
9391   {
9392     MovDelay[ax][ay]--;
9393     if (MovDelay[ax][ay])
9394       return;
9395   }
9396
9397   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9398   {
9399     int xx = ax+x1, yy = ay+y1;
9400     int old_element = Tile[xx][yy];
9401     int num_neighbours = 0;
9402
9403     if (!IN_LEV_FIELD(xx, yy))
9404       continue;
9405
9406     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9407     {
9408       int x = xx+x2, y = yy+y2;
9409
9410       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9411         continue;
9412
9413       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9414       boolean is_neighbour = FALSE;
9415
9416       if (level.use_life_bugs)
9417         is_neighbour =
9418           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9419            (IS_FREE(x, y)                             &&  Stop[x][y]));
9420       else
9421         is_neighbour =
9422           (Last[x][y] == element || is_player_cell);
9423
9424       if (is_neighbour)
9425         num_neighbours++;
9426     }
9427
9428     boolean is_free = FALSE;
9429
9430     if (level.use_life_bugs)
9431       is_free = (IS_FREE(xx, yy));
9432     else
9433       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9434
9435     if (xx == ax && yy == ay)           // field in the middle
9436     {
9437       if (num_neighbours < life_parameter[0] ||
9438           num_neighbours > life_parameter[1])
9439       {
9440         Tile[xx][yy] = EL_EMPTY;
9441         if (Tile[xx][yy] != old_element)
9442           TEST_DrawLevelField(xx, yy);
9443         Stop[xx][yy] = TRUE;
9444         changed = TRUE;
9445       }
9446     }
9447     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9448     {                                   // free border field
9449       if (num_neighbours >= life_parameter[2] &&
9450           num_neighbours <= life_parameter[3])
9451       {
9452         Tile[xx][yy] = element;
9453         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time - 1);
9454         if (Tile[xx][yy] != old_element)
9455           TEST_DrawLevelField(xx, yy);
9456         Stop[xx][yy] = TRUE;
9457         changed = TRUE;
9458       }
9459     }
9460   }
9461
9462   if (changed)
9463     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9464                    SND_GAME_OF_LIFE_GROWING);
9465 }
9466
9467 static void InitRobotWheel(int x, int y)
9468 {
9469   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9470 }
9471
9472 static void RunRobotWheel(int x, int y)
9473 {
9474   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9475 }
9476
9477 static void StopRobotWheel(int x, int y)
9478 {
9479   if (game.robot_wheel_x == x &&
9480       game.robot_wheel_y == y)
9481   {
9482     game.robot_wheel_x = -1;
9483     game.robot_wheel_y = -1;
9484     game.robot_wheel_active = FALSE;
9485   }
9486 }
9487
9488 static void InitTimegateWheel(int x, int y)
9489 {
9490   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9491 }
9492
9493 static void RunTimegateWheel(int x, int y)
9494 {
9495   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9496 }
9497
9498 static void InitMagicBallDelay(int x, int y)
9499 {
9500   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9501 }
9502
9503 static void ActivateMagicBall(int bx, int by)
9504 {
9505   int x, y;
9506
9507   if (level.ball_random)
9508   {
9509     int pos_border = RND(8);    // select one of the eight border elements
9510     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9511     int xx = pos_content % 3;
9512     int yy = pos_content / 3;
9513
9514     x = bx - 1 + xx;
9515     y = by - 1 + yy;
9516
9517     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9518       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9519   }
9520   else
9521   {
9522     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9523     {
9524       int xx = x - bx + 1;
9525       int yy = y - by + 1;
9526
9527       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9528         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9529     }
9530   }
9531
9532   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9533 }
9534
9535 static void CheckExit(int x, int y)
9536 {
9537   if (game.gems_still_needed > 0 ||
9538       game.sokoban_fields_still_needed > 0 ||
9539       game.sokoban_objects_still_needed > 0 ||
9540       game.lights_still_needed > 0)
9541   {
9542     int element = Tile[x][y];
9543     int graphic = el2img(element);
9544
9545     if (IS_ANIMATED(graphic))
9546       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9547
9548     return;
9549   }
9550
9551   // do not re-open exit door closed after last player
9552   if (game.all_players_gone)
9553     return;
9554
9555   Tile[x][y] = EL_EXIT_OPENING;
9556
9557   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9558 }
9559
9560 static void CheckExitEM(int x, int y)
9561 {
9562   if (game.gems_still_needed > 0 ||
9563       game.sokoban_fields_still_needed > 0 ||
9564       game.sokoban_objects_still_needed > 0 ||
9565       game.lights_still_needed > 0)
9566   {
9567     int element = Tile[x][y];
9568     int graphic = el2img(element);
9569
9570     if (IS_ANIMATED(graphic))
9571       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9572
9573     return;
9574   }
9575
9576   // do not re-open exit door closed after last player
9577   if (game.all_players_gone)
9578     return;
9579
9580   Tile[x][y] = EL_EM_EXIT_OPENING;
9581
9582   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9583 }
9584
9585 static void CheckExitSteel(int x, int y)
9586 {
9587   if (game.gems_still_needed > 0 ||
9588       game.sokoban_fields_still_needed > 0 ||
9589       game.sokoban_objects_still_needed > 0 ||
9590       game.lights_still_needed > 0)
9591   {
9592     int element = Tile[x][y];
9593     int graphic = el2img(element);
9594
9595     if (IS_ANIMATED(graphic))
9596       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9597
9598     return;
9599   }
9600
9601   // do not re-open exit door closed after last player
9602   if (game.all_players_gone)
9603     return;
9604
9605   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9606
9607   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9608 }
9609
9610 static void CheckExitSteelEM(int x, int y)
9611 {
9612   if (game.gems_still_needed > 0 ||
9613       game.sokoban_fields_still_needed > 0 ||
9614       game.sokoban_objects_still_needed > 0 ||
9615       game.lights_still_needed > 0)
9616   {
9617     int element = Tile[x][y];
9618     int graphic = el2img(element);
9619
9620     if (IS_ANIMATED(graphic))
9621       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9622
9623     return;
9624   }
9625
9626   // do not re-open exit door closed after last player
9627   if (game.all_players_gone)
9628     return;
9629
9630   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9631
9632   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9633 }
9634
9635 static void CheckExitSP(int x, int y)
9636 {
9637   if (game.gems_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_SP_EXIT_OPENING;
9653
9654   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9655 }
9656
9657 static void CloseAllOpenTimegates(void)
9658 {
9659   int x, y;
9660
9661   SCAN_PLAYFIELD(x, y)
9662   {
9663     int element = Tile[x][y];
9664
9665     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9666     {
9667       Tile[x][y] = EL_TIMEGATE_CLOSING;
9668
9669       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9670     }
9671   }
9672 }
9673
9674 static void DrawTwinkleOnField(int x, int y)
9675 {
9676   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9677     return;
9678
9679   if (Tile[x][y] == EL_BD_DIAMOND)
9680     return;
9681
9682   if (MovDelay[x][y] == 0)      // next animation frame
9683     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9684
9685   if (MovDelay[x][y] != 0)      // wait some time before next frame
9686   {
9687     MovDelay[x][y]--;
9688
9689     DrawLevelElementAnimation(x, y, Tile[x][y]);
9690
9691     if (MovDelay[x][y] != 0)
9692     {
9693       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9694                                            10 - MovDelay[x][y]);
9695
9696       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9697     }
9698   }
9699 }
9700
9701 static void WallGrowing(int x, int y)
9702 {
9703   int delay = 6;
9704
9705   if (!MovDelay[x][y])          // next animation frame
9706     MovDelay[x][y] = 3 * delay;
9707
9708   if (MovDelay[x][y])           // wait some time before next frame
9709   {
9710     MovDelay[x][y]--;
9711
9712     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9713     {
9714       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9715       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9716
9717       DrawLevelGraphic(x, y, graphic, frame);
9718     }
9719
9720     if (!MovDelay[x][y])
9721     {
9722       if (MovDir[x][y] == MV_LEFT)
9723       {
9724         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9725           TEST_DrawLevelField(x - 1, y);
9726       }
9727       else if (MovDir[x][y] == MV_RIGHT)
9728       {
9729         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9730           TEST_DrawLevelField(x + 1, y);
9731       }
9732       else if (MovDir[x][y] == MV_UP)
9733       {
9734         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9735           TEST_DrawLevelField(x, y - 1);
9736       }
9737       else
9738       {
9739         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9740           TEST_DrawLevelField(x, y + 1);
9741       }
9742
9743       Tile[x][y] = Store[x][y];
9744       Store[x][y] = 0;
9745       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9746       TEST_DrawLevelField(x, y);
9747     }
9748   }
9749 }
9750
9751 static void CheckWallGrowing(int ax, int ay)
9752 {
9753   int element = Tile[ax][ay];
9754   int graphic = el2img(element);
9755   boolean free_top    = FALSE;
9756   boolean free_bottom = FALSE;
9757   boolean free_left   = FALSE;
9758   boolean free_right  = FALSE;
9759   boolean stop_top    = FALSE;
9760   boolean stop_bottom = FALSE;
9761   boolean stop_left   = FALSE;
9762   boolean stop_right  = FALSE;
9763   boolean new_wall    = FALSE;
9764
9765   boolean is_steelwall  = (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9766                            element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9767                            element == EL_EXPANDABLE_STEELWALL_ANY);
9768
9769   boolean grow_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9770                              element == EL_EXPANDABLE_WALL_ANY ||
9771                              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9772                              element == EL_EXPANDABLE_STEELWALL_ANY);
9773
9774   boolean grow_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9775                              element == EL_EXPANDABLE_WALL_ANY ||
9776                              element == EL_EXPANDABLE_WALL ||
9777                              element == EL_BD_EXPANDABLE_WALL ||
9778                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9779                              element == EL_EXPANDABLE_STEELWALL_ANY);
9780
9781   boolean stop_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9782                              element == EL_EXPANDABLE_STEELWALL_VERTICAL);
9783
9784   boolean stop_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9785                              element == EL_EXPANDABLE_WALL ||
9786                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL);
9787
9788   int wall_growing = (is_steelwall ?
9789                       EL_EXPANDABLE_STEELWALL_GROWING :
9790                       EL_EXPANDABLE_WALL_GROWING);
9791
9792   int gfx_wall_growing_up    = (is_steelwall ?
9793                                 IMG_EXPANDABLE_STEELWALL_GROWING_UP :
9794                                 IMG_EXPANDABLE_WALL_GROWING_UP);
9795   int gfx_wall_growing_down  = (is_steelwall ?
9796                                 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN :
9797                                 IMG_EXPANDABLE_WALL_GROWING_DOWN);
9798   int gfx_wall_growing_left  = (is_steelwall ?
9799                                 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT :
9800                                 IMG_EXPANDABLE_WALL_GROWING_LEFT);
9801   int gfx_wall_growing_right = (is_steelwall ?
9802                                 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT :
9803                                 IMG_EXPANDABLE_WALL_GROWING_RIGHT);
9804
9805   if (IS_ANIMATED(graphic))
9806     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9807
9808   if (!MovDelay[ax][ay])        // start building new wall
9809     MovDelay[ax][ay] = 6;
9810
9811   if (MovDelay[ax][ay])         // wait some time before building new wall
9812   {
9813     MovDelay[ax][ay]--;
9814     if (MovDelay[ax][ay])
9815       return;
9816   }
9817
9818   if (IN_LEV_FIELD(ax, ay - 1) && IS_FREE(ax, ay - 1))
9819     free_top = TRUE;
9820   if (IN_LEV_FIELD(ax, ay + 1) && IS_FREE(ax, ay + 1))
9821     free_bottom = TRUE;
9822   if (IN_LEV_FIELD(ax - 1, ay) && IS_FREE(ax - 1, ay))
9823     free_left = TRUE;
9824   if (IN_LEV_FIELD(ax + 1, ay) && IS_FREE(ax + 1, ay))
9825     free_right = TRUE;
9826
9827   if (grow_vertical)
9828   {
9829     if (free_top)
9830     {
9831       Tile[ax][ay - 1] = wall_growing;
9832       Store[ax][ay - 1] = element;
9833       GfxDir[ax][ay - 1] = MovDir[ax][ay - 1] = MV_UP;
9834
9835       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay - 1)))
9836         DrawLevelGraphic(ax, ay - 1, gfx_wall_growing_up, 0);
9837
9838       new_wall = TRUE;
9839     }
9840
9841     if (free_bottom)
9842     {
9843       Tile[ax][ay + 1] = wall_growing;
9844       Store[ax][ay + 1] = element;
9845       GfxDir[ax][ay + 1] = MovDir[ax][ay + 1] = MV_DOWN;
9846
9847       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay + 1)))
9848         DrawLevelGraphic(ax, ay + 1, gfx_wall_growing_down, 0);
9849
9850       new_wall = TRUE;
9851     }
9852   }
9853
9854   if (grow_horizontal)
9855   {
9856     if (free_left)
9857     {
9858       Tile[ax - 1][ay] = wall_growing;
9859       Store[ax - 1][ay] = element;
9860       GfxDir[ax - 1][ay] = MovDir[ax - 1][ay] = MV_LEFT;
9861
9862       if (IN_SCR_FIELD(SCREENX(ax - 1), SCREENY(ay)))
9863         DrawLevelGraphic(ax - 1, ay, gfx_wall_growing_left, 0);
9864
9865       new_wall = TRUE;
9866     }
9867
9868     if (free_right)
9869     {
9870       Tile[ax + 1][ay] = wall_growing;
9871       Store[ax + 1][ay] = element;
9872       GfxDir[ax + 1][ay] = MovDir[ax + 1][ay] = MV_RIGHT;
9873
9874       if (IN_SCR_FIELD(SCREENX(ax + 1), SCREENY(ay)))
9875         DrawLevelGraphic(ax + 1, ay, gfx_wall_growing_right, 0);
9876
9877       new_wall = TRUE;
9878     }
9879   }
9880
9881   if (element == EL_EXPANDABLE_WALL && (free_left || free_right))
9882     TEST_DrawLevelField(ax, ay);
9883
9884   if (!IN_LEV_FIELD(ax, ay - 1) || IS_WALL(Tile[ax][ay - 1]))
9885     stop_top = TRUE;
9886   if (!IN_LEV_FIELD(ax, ay + 1) || IS_WALL(Tile[ax][ay + 1]))
9887     stop_bottom = TRUE;
9888   if (!IN_LEV_FIELD(ax - 1, ay) || IS_WALL(Tile[ax - 1][ay]))
9889     stop_left = TRUE;
9890   if (!IN_LEV_FIELD(ax + 1, ay) || IS_WALL(Tile[ax + 1][ay]))
9891     stop_right = TRUE;
9892
9893   if (((stop_top && stop_bottom) || stop_horizontal) &&
9894       ((stop_left && stop_right) || stop_vertical))
9895     Tile[ax][ay] = (is_steelwall ? EL_STEELWALL : EL_WALL);
9896
9897   if (new_wall)
9898     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9899 }
9900
9901 static void CheckForDragon(int x, int y)
9902 {
9903   int i, j;
9904   boolean dragon_found = FALSE;
9905   struct XY *xy = xy_topdown;
9906
9907   for (i = 0; i < NUM_DIRECTIONS; i++)
9908   {
9909     for (j = 0; j < 4; j++)
9910     {
9911       int xx = x + j * xy[i].x;
9912       int yy = y + j * xy[i].y;
9913
9914       if (IN_LEV_FIELD(xx, yy) &&
9915           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9916       {
9917         if (Tile[xx][yy] == EL_DRAGON)
9918           dragon_found = TRUE;
9919       }
9920       else
9921         break;
9922     }
9923   }
9924
9925   if (!dragon_found)
9926   {
9927     for (i = 0; i < NUM_DIRECTIONS; i++)
9928     {
9929       for (j = 0; j < 3; j++)
9930       {
9931         int xx = x + j * xy[i].x;
9932         int yy = y + j * xy[i].y;
9933
9934         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9935         {
9936           Tile[xx][yy] = EL_EMPTY;
9937           TEST_DrawLevelField(xx, yy);
9938         }
9939         else
9940           break;
9941       }
9942     }
9943   }
9944 }
9945
9946 static void InitBuggyBase(int x, int y)
9947 {
9948   int element = Tile[x][y];
9949   int activating_delay = FRAMES_PER_SECOND / 4;
9950
9951   ChangeDelay[x][y] =
9952     (element == EL_SP_BUGGY_BASE ?
9953      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9954      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9955      activating_delay :
9956      element == EL_SP_BUGGY_BASE_ACTIVE ?
9957      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9958 }
9959
9960 static void WarnBuggyBase(int x, int y)
9961 {
9962   int i;
9963   struct XY *xy = xy_topdown;
9964
9965   for (i = 0; i < NUM_DIRECTIONS; i++)
9966   {
9967     int xx = x + xy[i].x;
9968     int yy = y + xy[i].y;
9969
9970     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9971     {
9972       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9973
9974       break;
9975     }
9976   }
9977 }
9978
9979 static void InitTrap(int x, int y)
9980 {
9981   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9982 }
9983
9984 static void ActivateTrap(int x, int y)
9985 {
9986   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9987 }
9988
9989 static void ChangeActiveTrap(int x, int y)
9990 {
9991   int graphic = IMG_TRAP_ACTIVE;
9992
9993   // if new animation frame was drawn, correct crumbled sand border
9994   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9995     TEST_DrawLevelFieldCrumbled(x, y);
9996 }
9997
9998 static int getSpecialActionElement(int element, int number, int base_element)
9999 {
10000   return (element != EL_EMPTY ? element :
10001           number != -1 ? base_element + number - 1 :
10002           EL_EMPTY);
10003 }
10004
10005 static int getModifiedActionNumber(int value_old, int operator, int operand,
10006                                    int value_min, int value_max)
10007 {
10008   int value_new = (operator == CA_MODE_SET      ? operand :
10009                    operator == CA_MODE_ADD      ? value_old + operand :
10010                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10011                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10012                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10013                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10014                    value_old);
10015
10016   return (value_new < value_min ? value_min :
10017           value_new > value_max ? value_max :
10018           value_new);
10019 }
10020
10021 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10022 {
10023   struct ElementInfo *ei = &element_info[element];
10024   struct ElementChangeInfo *change = &ei->change_page[page];
10025   int target_element = change->target_element;
10026   int action_type = change->action_type;
10027   int action_mode = change->action_mode;
10028   int action_arg = change->action_arg;
10029   int action_element = change->action_element;
10030   int i;
10031
10032   if (!change->has_action)
10033     return;
10034
10035   // ---------- determine action paramater values -----------------------------
10036
10037   int level_time_value =
10038     (level.time > 0 ? TimeLeft :
10039      TimePlayed);
10040
10041   int action_arg_element_raw =
10042     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10043      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10044      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10045      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10046      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10047      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10048      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10049      EL_EMPTY);
10050   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10051
10052   int action_arg_direction =
10053     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10054      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10055      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10056      change->actual_trigger_side :
10057      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10058      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10059      MV_NONE);
10060
10061   int action_arg_number_min =
10062     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10063      CA_ARG_MIN);
10064
10065   int action_arg_number_max =
10066     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10067      action_type == CA_SET_LEVEL_GEMS ? 999 :
10068      action_type == CA_SET_LEVEL_TIME ? 9999 :
10069      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10070      action_type == CA_SET_CE_VALUE ? 9999 :
10071      action_type == CA_SET_CE_SCORE ? 9999 :
10072      CA_ARG_MAX);
10073
10074   int action_arg_number_reset =
10075     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10076      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10077      action_type == CA_SET_LEVEL_TIME ? level.time :
10078      action_type == CA_SET_LEVEL_SCORE ? 0 :
10079      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10080      action_type == CA_SET_CE_SCORE ? 0 :
10081      0);
10082
10083   int action_arg_number =
10084     (action_arg <= CA_ARG_MAX ? action_arg :
10085      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10086      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10087      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10088      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10089      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10090      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10091      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10092      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10093      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10094      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10095      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10096      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10097      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10098      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10099      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10100      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10101      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10102      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10103      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10104      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10105      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10106      -1);
10107
10108   int action_arg_number_old =
10109     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10110      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10111      action_type == CA_SET_LEVEL_SCORE ? game.score :
10112      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10113      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10114      0);
10115
10116   int action_arg_number_new =
10117     getModifiedActionNumber(action_arg_number_old,
10118                             action_mode, action_arg_number,
10119                             action_arg_number_min, action_arg_number_max);
10120
10121   int trigger_player_bits =
10122     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10123      change->actual_trigger_player_bits : change->trigger_player);
10124
10125   int action_arg_player_bits =
10126     (action_arg >= CA_ARG_PLAYER_1 &&
10127      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10128      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10129      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10130      PLAYER_BITS_ANY);
10131
10132   // ---------- execute action  -----------------------------------------------
10133
10134   switch (action_type)
10135   {
10136     case CA_NO_ACTION:
10137     {
10138       return;
10139     }
10140
10141     // ---------- level actions  ----------------------------------------------
10142
10143     case CA_RESTART_LEVEL:
10144     {
10145       game.restart_level = TRUE;
10146
10147       break;
10148     }
10149
10150     case CA_SHOW_ENVELOPE:
10151     {
10152       int element = getSpecialActionElement(action_arg_element,
10153                                             action_arg_number, EL_ENVELOPE_1);
10154
10155       if (IS_ENVELOPE(element))
10156         local_player->show_envelope = element;
10157
10158       break;
10159     }
10160
10161     case CA_SET_LEVEL_TIME:
10162     {
10163       if (level.time > 0)       // only modify limited time value
10164       {
10165         TimeLeft = action_arg_number_new;
10166
10167         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10168
10169         DisplayGameControlValues();
10170
10171         if (!TimeLeft && game.time_limit)
10172           for (i = 0; i < MAX_PLAYERS; i++)
10173             KillPlayer(&stored_player[i]);
10174       }
10175
10176       break;
10177     }
10178
10179     case CA_SET_LEVEL_SCORE:
10180     {
10181       game.score = action_arg_number_new;
10182
10183       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10184
10185       DisplayGameControlValues();
10186
10187       break;
10188     }
10189
10190     case CA_SET_LEVEL_GEMS:
10191     {
10192       game.gems_still_needed = action_arg_number_new;
10193
10194       game.snapshot.collected_item = TRUE;
10195
10196       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10197
10198       DisplayGameControlValues();
10199
10200       break;
10201     }
10202
10203     case CA_SET_LEVEL_WIND:
10204     {
10205       game.wind_direction = action_arg_direction;
10206
10207       break;
10208     }
10209
10210     case CA_SET_LEVEL_RANDOM_SEED:
10211     {
10212       // ensure that setting a new random seed while playing is predictable
10213       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10214
10215       break;
10216     }
10217
10218     // ---------- player actions  ---------------------------------------------
10219
10220     case CA_MOVE_PLAYER:
10221     case CA_MOVE_PLAYER_NEW:
10222     {
10223       // automatically move to the next field in specified direction
10224       for (i = 0; i < MAX_PLAYERS; i++)
10225         if (trigger_player_bits & (1 << i))
10226           if (action_type == CA_MOVE_PLAYER ||
10227               stored_player[i].MovPos == 0)
10228             stored_player[i].programmed_action = action_arg_direction;
10229
10230       break;
10231     }
10232
10233     case CA_EXIT_PLAYER:
10234     {
10235       for (i = 0; i < MAX_PLAYERS; i++)
10236         if (action_arg_player_bits & (1 << i))
10237           ExitPlayer(&stored_player[i]);
10238
10239       if (game.players_still_needed == 0)
10240         LevelSolved();
10241
10242       break;
10243     }
10244
10245     case CA_KILL_PLAYER:
10246     {
10247       for (i = 0; i < MAX_PLAYERS; i++)
10248         if (action_arg_player_bits & (1 << i))
10249           KillPlayer(&stored_player[i]);
10250
10251       break;
10252     }
10253
10254     case CA_SET_PLAYER_KEYS:
10255     {
10256       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10257       int element = getSpecialActionElement(action_arg_element,
10258                                             action_arg_number, EL_KEY_1);
10259
10260       if (IS_KEY(element))
10261       {
10262         for (i = 0; i < MAX_PLAYERS; i++)
10263         {
10264           if (trigger_player_bits & (1 << i))
10265           {
10266             stored_player[i].key[KEY_NR(element)] = key_state;
10267
10268             DrawGameDoorValues();
10269           }
10270         }
10271       }
10272
10273       break;
10274     }
10275
10276     case CA_SET_PLAYER_SPEED:
10277     {
10278       for (i = 0; i < MAX_PLAYERS; i++)
10279       {
10280         if (trigger_player_bits & (1 << i))
10281         {
10282           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10283
10284           if (action_arg == CA_ARG_SPEED_FASTER &&
10285               stored_player[i].cannot_move)
10286           {
10287             action_arg_number = STEPSIZE_VERY_SLOW;
10288           }
10289           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10290                    action_arg == CA_ARG_SPEED_FASTER)
10291           {
10292             action_arg_number = 2;
10293             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10294                            CA_MODE_MULTIPLY);
10295           }
10296           else if (action_arg == CA_ARG_NUMBER_RESET)
10297           {
10298             action_arg_number = level.initial_player_stepsize[i];
10299           }
10300
10301           move_stepsize =
10302             getModifiedActionNumber(move_stepsize,
10303                                     action_mode,
10304                                     action_arg_number,
10305                                     action_arg_number_min,
10306                                     action_arg_number_max);
10307
10308           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10309         }
10310       }
10311
10312       break;
10313     }
10314
10315     case CA_SET_PLAYER_SHIELD:
10316     {
10317       for (i = 0; i < MAX_PLAYERS; i++)
10318       {
10319         if (trigger_player_bits & (1 << i))
10320         {
10321           if (action_arg == CA_ARG_SHIELD_OFF)
10322           {
10323             stored_player[i].shield_normal_time_left = 0;
10324             stored_player[i].shield_deadly_time_left = 0;
10325           }
10326           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10327           {
10328             stored_player[i].shield_normal_time_left = 999999;
10329           }
10330           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10331           {
10332             stored_player[i].shield_normal_time_left = 999999;
10333             stored_player[i].shield_deadly_time_left = 999999;
10334           }
10335         }
10336       }
10337
10338       break;
10339     }
10340
10341     case CA_SET_PLAYER_GRAVITY:
10342     {
10343       for (i = 0; i < MAX_PLAYERS; i++)
10344       {
10345         if (trigger_player_bits & (1 << i))
10346         {
10347           stored_player[i].gravity =
10348             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10349              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10350              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10351              stored_player[i].gravity);
10352         }
10353       }
10354
10355       break;
10356     }
10357
10358     case CA_SET_PLAYER_ARTWORK:
10359     {
10360       for (i = 0; i < MAX_PLAYERS; i++)
10361       {
10362         if (trigger_player_bits & (1 << i))
10363         {
10364           int artwork_element = action_arg_element;
10365
10366           if (action_arg == CA_ARG_ELEMENT_RESET)
10367             artwork_element =
10368               (level.use_artwork_element[i] ? level.artwork_element[i] :
10369                stored_player[i].element_nr);
10370
10371           if (stored_player[i].artwork_element != artwork_element)
10372             stored_player[i].Frame = 0;
10373
10374           stored_player[i].artwork_element = artwork_element;
10375
10376           SetPlayerWaiting(&stored_player[i], FALSE);
10377
10378           // set number of special actions for bored and sleeping animation
10379           stored_player[i].num_special_action_bored =
10380             get_num_special_action(artwork_element,
10381                                    ACTION_BORING_1, ACTION_BORING_LAST);
10382           stored_player[i].num_special_action_sleeping =
10383             get_num_special_action(artwork_element,
10384                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10385         }
10386       }
10387
10388       break;
10389     }
10390
10391     case CA_SET_PLAYER_INVENTORY:
10392     {
10393       for (i = 0; i < MAX_PLAYERS; i++)
10394       {
10395         struct PlayerInfo *player = &stored_player[i];
10396         int j, k;
10397
10398         if (trigger_player_bits & (1 << i))
10399         {
10400           int inventory_element = action_arg_element;
10401
10402           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10403               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10404               action_arg == CA_ARG_ELEMENT_ACTION)
10405           {
10406             int element = inventory_element;
10407             int collect_count = element_info[element].collect_count_initial;
10408
10409             if (!IS_CUSTOM_ELEMENT(element))
10410               collect_count = 1;
10411
10412             if (collect_count == 0)
10413               player->inventory_infinite_element = element;
10414             else
10415               for (k = 0; k < collect_count; k++)
10416                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10417                   player->inventory_element[player->inventory_size++] =
10418                     element;
10419           }
10420           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10421                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10422                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10423           {
10424             if (player->inventory_infinite_element != EL_UNDEFINED &&
10425                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10426                                      action_arg_element_raw))
10427               player->inventory_infinite_element = EL_UNDEFINED;
10428
10429             for (k = 0, j = 0; j < player->inventory_size; j++)
10430             {
10431               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10432                                         action_arg_element_raw))
10433                 player->inventory_element[k++] = player->inventory_element[j];
10434             }
10435
10436             player->inventory_size = k;
10437           }
10438           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10439           {
10440             if (player->inventory_size > 0)
10441             {
10442               for (j = 0; j < player->inventory_size - 1; j++)
10443                 player->inventory_element[j] = player->inventory_element[j + 1];
10444
10445               player->inventory_size--;
10446             }
10447           }
10448           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10449           {
10450             if (player->inventory_size > 0)
10451               player->inventory_size--;
10452           }
10453           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10454           {
10455             player->inventory_infinite_element = EL_UNDEFINED;
10456             player->inventory_size = 0;
10457           }
10458           else if (action_arg == CA_ARG_INVENTORY_RESET)
10459           {
10460             player->inventory_infinite_element = EL_UNDEFINED;
10461             player->inventory_size = 0;
10462
10463             if (level.use_initial_inventory[i])
10464             {
10465               for (j = 0; j < level.initial_inventory_size[i]; j++)
10466               {
10467                 int element = level.initial_inventory_content[i][j];
10468                 int collect_count = element_info[element].collect_count_initial;
10469
10470                 if (!IS_CUSTOM_ELEMENT(element))
10471                   collect_count = 1;
10472
10473                 if (collect_count == 0)
10474                   player->inventory_infinite_element = element;
10475                 else
10476                   for (k = 0; k < collect_count; k++)
10477                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10478                       player->inventory_element[player->inventory_size++] =
10479                         element;
10480               }
10481             }
10482           }
10483         }
10484       }
10485
10486       break;
10487     }
10488
10489     // ---------- CE actions  -------------------------------------------------
10490
10491     case CA_SET_CE_VALUE:
10492     {
10493       int last_ce_value = CustomValue[x][y];
10494
10495       CustomValue[x][y] = action_arg_number_new;
10496
10497       if (CustomValue[x][y] != last_ce_value)
10498       {
10499         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10500         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10501
10502         if (CustomValue[x][y] == 0)
10503         {
10504           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10505           ChangeCount[x][y] = 0;        // allow at least one more change
10506
10507           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10508           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10509         }
10510       }
10511
10512       break;
10513     }
10514
10515     case CA_SET_CE_SCORE:
10516     {
10517       int last_ce_score = ei->collect_score;
10518
10519       ei->collect_score = action_arg_number_new;
10520
10521       if (ei->collect_score != last_ce_score)
10522       {
10523         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10524         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10525
10526         if (ei->collect_score == 0)
10527         {
10528           int xx, yy;
10529
10530           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10531           ChangeCount[x][y] = 0;        // allow at least one more change
10532
10533           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10534           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10535
10536           /*
10537             This is a very special case that seems to be a mixture between
10538             CheckElementChange() and CheckTriggeredElementChange(): while
10539             the first one only affects single elements that are triggered
10540             directly, the second one affects multiple elements in the playfield
10541             that are triggered indirectly by another element. This is a third
10542             case: Changing the CE score always affects multiple identical CEs,
10543             so every affected CE must be checked, not only the single CE for
10544             which the CE score was changed in the first place (as every instance
10545             of that CE shares the same CE score, and therefore also can change)!
10546           */
10547           SCAN_PLAYFIELD(xx, yy)
10548           {
10549             if (Tile[xx][yy] == element)
10550               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10551                                  CE_SCORE_GETS_ZERO);
10552           }
10553         }
10554       }
10555
10556       break;
10557     }
10558
10559     case CA_SET_CE_ARTWORK:
10560     {
10561       int artwork_element = action_arg_element;
10562       boolean reset_frame = FALSE;
10563       int xx, yy;
10564
10565       if (action_arg == CA_ARG_ELEMENT_RESET)
10566         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10567                            element);
10568
10569       if (ei->gfx_element != artwork_element)
10570         reset_frame = TRUE;
10571
10572       ei->gfx_element = artwork_element;
10573
10574       SCAN_PLAYFIELD(xx, yy)
10575       {
10576         if (Tile[xx][yy] == element)
10577         {
10578           if (reset_frame)
10579           {
10580             ResetGfxAnimation(xx, yy);
10581             ResetRandomAnimationValue(xx, yy);
10582           }
10583
10584           TEST_DrawLevelField(xx, yy);
10585         }
10586       }
10587
10588       break;
10589     }
10590
10591     // ---------- engine actions  ---------------------------------------------
10592
10593     case CA_SET_ENGINE_SCAN_MODE:
10594     {
10595       InitPlayfieldScanMode(action_arg);
10596
10597       break;
10598     }
10599
10600     default:
10601       break;
10602   }
10603 }
10604
10605 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10606 {
10607   int old_element = Tile[x][y];
10608   int new_element = GetElementFromGroupElement(element);
10609   int previous_move_direction = MovDir[x][y];
10610   int last_ce_value = CustomValue[x][y];
10611   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10612   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10613   boolean add_player_onto_element = (new_element_is_player &&
10614                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10615                                      IS_WALKABLE(old_element));
10616
10617   if (!add_player_onto_element)
10618   {
10619     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10620       RemoveMovingField(x, y);
10621     else
10622       RemoveField(x, y);
10623
10624     Tile[x][y] = new_element;
10625
10626     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10627       MovDir[x][y] = previous_move_direction;
10628
10629     if (element_info[new_element].use_last_ce_value)
10630       CustomValue[x][y] = last_ce_value;
10631
10632     InitField_WithBug1(x, y, FALSE);
10633
10634     new_element = Tile[x][y];   // element may have changed
10635
10636     ResetGfxAnimation(x, y);
10637     ResetRandomAnimationValue(x, y);
10638
10639     TEST_DrawLevelField(x, y);
10640
10641     if (GFX_CRUMBLED(new_element))
10642       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10643
10644     if (old_element == EL_EXPLOSION)
10645     {
10646       Store[x][y] = Store2[x][y] = 0;
10647
10648       // check if new element replaces an exploding player, requiring cleanup
10649       if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
10650         StorePlayer[x][y] = 0;
10651     }
10652
10653     // check if element under the player changes from accessible to unaccessible
10654     // (needed for special case of dropping element which then changes)
10655     // (must be checked after creating new element for walkable group elements)
10656     if (IS_PLAYER(x, y) && !player_explosion_protected &&
10657         IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10658     {
10659       KillPlayer(PLAYERINFO(x, y));
10660
10661       return;
10662     }
10663   }
10664
10665   // "ChangeCount" not set yet to allow "entered by player" change one time
10666   if (new_element_is_player)
10667     RelocatePlayer(x, y, new_element);
10668
10669   if (is_change)
10670     ChangeCount[x][y]++;        // count number of changes in the same frame
10671
10672   TestIfBadThingTouchesPlayer(x, y);
10673   TestIfPlayerTouchesCustomElement(x, y);
10674   TestIfElementTouchesCustomElement(x, y);
10675 }
10676
10677 static void CreateField(int x, int y, int element)
10678 {
10679   CreateFieldExt(x, y, element, FALSE);
10680 }
10681
10682 static void CreateElementFromChange(int x, int y, int element)
10683 {
10684   element = GET_VALID_RUNTIME_ELEMENT(element);
10685
10686   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10687   {
10688     int old_element = Tile[x][y];
10689
10690     // prevent changed element from moving in same engine frame
10691     // unless both old and new element can either fall or move
10692     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10693         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10694       Stop[x][y] = TRUE;
10695   }
10696
10697   CreateFieldExt(x, y, element, TRUE);
10698 }
10699
10700 static boolean ChangeElement(int x, int y, int element, int page)
10701 {
10702   struct ElementInfo *ei = &element_info[element];
10703   struct ElementChangeInfo *change = &ei->change_page[page];
10704   int ce_value = CustomValue[x][y];
10705   int ce_score = ei->collect_score;
10706   int target_element;
10707   int old_element = Tile[x][y];
10708
10709   // always use default change event to prevent running into a loop
10710   if (ChangeEvent[x][y] == -1)
10711     ChangeEvent[x][y] = CE_DELAY;
10712
10713   if (ChangeEvent[x][y] == CE_DELAY)
10714   {
10715     // reset actual trigger element, trigger player and action element
10716     change->actual_trigger_element = EL_EMPTY;
10717     change->actual_trigger_player = EL_EMPTY;
10718     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10719     change->actual_trigger_side = CH_SIDE_NONE;
10720     change->actual_trigger_ce_value = 0;
10721     change->actual_trigger_ce_score = 0;
10722   }
10723
10724   // do not change elements more than a specified maximum number of changes
10725   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10726     return FALSE;
10727
10728   ChangeCount[x][y]++;          // count number of changes in the same frame
10729
10730   if (ei->has_anim_event)
10731     HandleGlobalAnimEventByElementChange(element, page, x, y);
10732
10733   if (change->explode)
10734   {
10735     Bang(x, y);
10736
10737     return TRUE;
10738   }
10739
10740   if (change->use_target_content)
10741   {
10742     boolean complete_replace = TRUE;
10743     boolean can_replace[3][3];
10744     int xx, yy;
10745
10746     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10747     {
10748       boolean is_empty;
10749       boolean is_walkable;
10750       boolean is_diggable;
10751       boolean is_collectible;
10752       boolean is_removable;
10753       boolean is_destructible;
10754       int ex = x + xx - 1;
10755       int ey = y + yy - 1;
10756       int content_element = change->target_content.e[xx][yy];
10757       int e;
10758
10759       can_replace[xx][yy] = TRUE;
10760
10761       if (ex == x && ey == y)   // do not check changing element itself
10762         continue;
10763
10764       if (content_element == EL_EMPTY_SPACE)
10765       {
10766         can_replace[xx][yy] = FALSE;    // do not replace border with space
10767
10768         continue;
10769       }
10770
10771       if (!IN_LEV_FIELD(ex, ey))
10772       {
10773         can_replace[xx][yy] = FALSE;
10774         complete_replace = FALSE;
10775
10776         continue;
10777       }
10778
10779       e = Tile[ex][ey];
10780
10781       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10782         e = MovingOrBlocked2Element(ex, ey);
10783
10784       is_empty = (IS_FREE(ex, ey) ||
10785                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10786
10787       is_walkable     = (is_empty || IS_WALKABLE(e));
10788       is_diggable     = (is_empty || IS_DIGGABLE(e));
10789       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10790       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10791       is_removable    = (is_diggable || is_collectible);
10792
10793       can_replace[xx][yy] =
10794         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10795           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10796           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10797           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10798           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10799           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10800          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10801
10802       if (!can_replace[xx][yy])
10803         complete_replace = FALSE;
10804     }
10805
10806     if (!change->only_if_complete || complete_replace)
10807     {
10808       boolean something_has_changed = FALSE;
10809
10810       if (change->only_if_complete && change->use_random_replace &&
10811           RND(100) < change->random_percentage)
10812         return FALSE;
10813
10814       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10815       {
10816         int ex = x + xx - 1;
10817         int ey = y + yy - 1;
10818         int content_element;
10819
10820         if (can_replace[xx][yy] && (!change->use_random_replace ||
10821                                     RND(100) < change->random_percentage))
10822         {
10823           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10824             RemoveMovingField(ex, ey);
10825
10826           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10827
10828           content_element = change->target_content.e[xx][yy];
10829           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10830                                               ce_value, ce_score);
10831
10832           CreateElementFromChange(ex, ey, target_element);
10833
10834           something_has_changed = TRUE;
10835
10836           // for symmetry reasons, freeze newly created border elements
10837           if (ex != x || ey != y)
10838             Stop[ex][ey] = TRUE;        // no more moving in this frame
10839         }
10840       }
10841
10842       if (something_has_changed)
10843       {
10844         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10845         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10846       }
10847     }
10848   }
10849   else
10850   {
10851     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10852                                         ce_value, ce_score);
10853
10854     if (element == EL_DIAGONAL_GROWING ||
10855         element == EL_DIAGONAL_SHRINKING)
10856     {
10857       target_element = Store[x][y];
10858
10859       Store[x][y] = EL_EMPTY;
10860     }
10861
10862     // special case: element changes to player (and may be kept if walkable)
10863     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
10864       CreateElementFromChange(x, y, EL_EMPTY);
10865
10866     CreateElementFromChange(x, y, target_element);
10867
10868     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10869     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10870   }
10871
10872   // this uses direct change before indirect change
10873   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10874
10875   return TRUE;
10876 }
10877
10878 static void HandleElementChange(int x, int y, int page)
10879 {
10880   int element = MovingOrBlocked2Element(x, y);
10881   struct ElementInfo *ei = &element_info[element];
10882   struct ElementChangeInfo *change = &ei->change_page[page];
10883   boolean handle_action_before_change = FALSE;
10884
10885 #ifdef DEBUG
10886   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10887       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10888   {
10889     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10890           x, y, element, element_info[element].token_name);
10891     Debug("game:playing:HandleElementChange", "This should never happen!");
10892   }
10893 #endif
10894
10895   // this can happen with classic bombs on walkable, changing elements
10896   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10897   {
10898     return;
10899   }
10900
10901   if (ChangeDelay[x][y] == 0)           // initialize element change
10902   {
10903     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10904
10905     if (change->can_change)
10906     {
10907       // !!! not clear why graphic animation should be reset at all here !!!
10908       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10909       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10910
10911       /*
10912         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10913
10914         When using an animation frame delay of 1 (this only happens with
10915         "sp_zonk.moving.left/right" in the classic graphics), the default
10916         (non-moving) animation shows wrong animation frames (while the
10917         moving animation, like "sp_zonk.moving.left/right", is correct,
10918         so this graphical bug never shows up with the classic graphics).
10919         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10920         be drawn instead of the correct frames 0,1,2,3. This is caused by
10921         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10922         an element change: First when the change delay ("ChangeDelay[][]")
10923         counter has reached zero after decrementing, then a second time in
10924         the next frame (after "GfxFrame[][]" was already incremented) when
10925         "ChangeDelay[][]" is reset to the initial delay value again.
10926
10927         This causes frame 0 to be drawn twice, while the last frame won't
10928         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10929
10930         As some animations may already be cleverly designed around this bug
10931         (at least the "Snake Bite" snake tail animation does this), it cannot
10932         simply be fixed here without breaking such existing animations.
10933         Unfortunately, it cannot easily be detected if a graphics set was
10934         designed "before" or "after" the bug was fixed. As a workaround,
10935         a new graphics set option "game.graphics_engine_version" was added
10936         to be able to specify the game's major release version for which the
10937         graphics set was designed, which can then be used to decide if the
10938         bugfix should be used (version 4 and above) or not (version 3 or
10939         below, or if no version was specified at all, as with old sets).
10940
10941         (The wrong/fixed animation frames can be tested with the test level set
10942         "test_gfxframe" and level "000", which contains a specially prepared
10943         custom element at level position (x/y) == (11/9) which uses the zonk
10944         animation mentioned above. Using "game.graphics_engine_version: 4"
10945         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10946         This can also be seen from the debug output for this test element.)
10947       */
10948
10949       // when a custom element is about to change (for example by change delay),
10950       // do not reset graphic animation when the custom element is moving
10951       if (game.graphics_engine_version < 4 &&
10952           !IS_MOVING(x, y))
10953       {
10954         ResetGfxAnimation(x, y);
10955         ResetRandomAnimationValue(x, y);
10956       }
10957
10958       if (change->pre_change_function)
10959         change->pre_change_function(x, y);
10960     }
10961   }
10962
10963   ChangeDelay[x][y]--;
10964
10965   if (ChangeDelay[x][y] != 0)           // continue element change
10966   {
10967     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10968
10969     // also needed if CE can not change, but has CE delay with CE action
10970     if (IS_ANIMATED(graphic))
10971       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10972
10973     if (change->can_change)
10974     {
10975       if (change->change_function)
10976         change->change_function(x, y);
10977     }
10978   }
10979   else                                  // finish element change
10980   {
10981     if (ChangePage[x][y] != -1)         // remember page from delayed change
10982     {
10983       page = ChangePage[x][y];
10984       ChangePage[x][y] = -1;
10985
10986       change = &ei->change_page[page];
10987     }
10988
10989     if (IS_MOVING(x, y))                // never change a running system ;-)
10990     {
10991       ChangeDelay[x][y] = 1;            // try change after next move step
10992       ChangePage[x][y] = page;          // remember page to use for change
10993
10994       return;
10995     }
10996
10997     // special case: set new level random seed before changing element
10998     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10999       handle_action_before_change = TRUE;
11000
11001     if (change->has_action && handle_action_before_change)
11002       ExecuteCustomElementAction(x, y, element, page);
11003
11004     if (change->can_change)
11005     {
11006       if (ChangeElement(x, y, element, page))
11007       {
11008         if (change->post_change_function)
11009           change->post_change_function(x, y);
11010       }
11011     }
11012
11013     if (change->has_action && !handle_action_before_change)
11014       ExecuteCustomElementAction(x, y, element, page);
11015   }
11016 }
11017
11018 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11019                                               int trigger_element,
11020                                               int trigger_event,
11021                                               int trigger_player,
11022                                               int trigger_side,
11023                                               int trigger_page)
11024 {
11025   boolean change_done_any = FALSE;
11026   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11027   int i;
11028
11029   if (!(trigger_events[trigger_element][trigger_event]))
11030     return FALSE;
11031
11032   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11033
11034   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11035   {
11036     int element = EL_CUSTOM_START + i;
11037     boolean change_done = FALSE;
11038     int p;
11039
11040     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11041         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11042       continue;
11043
11044     for (p = 0; p < element_info[element].num_change_pages; p++)
11045     {
11046       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11047
11048       if (change->can_change_or_has_action &&
11049           change->has_event[trigger_event] &&
11050           change->trigger_side & trigger_side &&
11051           change->trigger_player & trigger_player &&
11052           change->trigger_page & trigger_page_bits &&
11053           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11054       {
11055         change->actual_trigger_element = trigger_element;
11056         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11057         change->actual_trigger_player_bits = trigger_player;
11058         change->actual_trigger_side = trigger_side;
11059         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11060         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11061
11062         if ((change->can_change && !change_done) || change->has_action)
11063         {
11064           int x, y;
11065
11066           SCAN_PLAYFIELD(x, y)
11067           {
11068             if (Tile[x][y] == element)
11069             {
11070               if (change->can_change && !change_done)
11071               {
11072                 // if element already changed in this frame, not only prevent
11073                 // another element change (checked in ChangeElement()), but
11074                 // also prevent additional element actions for this element
11075
11076                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11077                     !level.use_action_after_change_bug)
11078                   continue;
11079
11080                 ChangeDelay[x][y] = 1;
11081                 ChangeEvent[x][y] = trigger_event;
11082
11083                 HandleElementChange(x, y, p);
11084               }
11085               else if (change->has_action)
11086               {
11087                 // if element already changed in this frame, not only prevent
11088                 // another element change (checked in ChangeElement()), but
11089                 // also prevent additional element actions for this element
11090
11091                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11092                     !level.use_action_after_change_bug)
11093                   continue;
11094
11095                 ExecuteCustomElementAction(x, y, element, p);
11096                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11097               }
11098             }
11099           }
11100
11101           if (change->can_change)
11102           {
11103             change_done = TRUE;
11104             change_done_any = TRUE;
11105           }
11106         }
11107       }
11108     }
11109   }
11110
11111   RECURSION_LOOP_DETECTION_END();
11112
11113   return change_done_any;
11114 }
11115
11116 static boolean CheckElementChangeExt(int x, int y,
11117                                      int element,
11118                                      int trigger_element,
11119                                      int trigger_event,
11120                                      int trigger_player,
11121                                      int trigger_side)
11122 {
11123   boolean change_done = FALSE;
11124   int p;
11125
11126   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11127       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11128     return FALSE;
11129
11130   if (Tile[x][y] == EL_BLOCKED)
11131   {
11132     Blocked2Moving(x, y, &x, &y);
11133     element = Tile[x][y];
11134   }
11135
11136   // check if element has already changed or is about to change after moving
11137   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11138        Tile[x][y] != element) ||
11139
11140       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11141        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11142         ChangePage[x][y] != -1)))
11143     return FALSE;
11144
11145   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11146
11147   for (p = 0; p < element_info[element].num_change_pages; p++)
11148   {
11149     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11150
11151     /* check trigger element for all events where the element that is checked
11152        for changing interacts with a directly adjacent element -- this is
11153        different to element changes that affect other elements to change on the
11154        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11155     boolean check_trigger_element =
11156       (trigger_event == CE_NEXT_TO_X ||
11157        trigger_event == CE_TOUCHING_X ||
11158        trigger_event == CE_HITTING_X ||
11159        trigger_event == CE_HIT_BY_X ||
11160        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11161
11162     if (change->can_change_or_has_action &&
11163         change->has_event[trigger_event] &&
11164         change->trigger_side & trigger_side &&
11165         change->trigger_player & trigger_player &&
11166         (!check_trigger_element ||
11167          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11168     {
11169       change->actual_trigger_element = trigger_element;
11170       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11171       change->actual_trigger_player_bits = trigger_player;
11172       change->actual_trigger_side = trigger_side;
11173       change->actual_trigger_ce_value = CustomValue[x][y];
11174       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11175
11176       // special case: trigger element not at (x,y) position for some events
11177       if (check_trigger_element)
11178       {
11179         static struct
11180         {
11181           int dx, dy;
11182         } move_xy[] =
11183           {
11184             {  0,  0 },
11185             { -1,  0 },
11186             { +1,  0 },
11187             {  0,  0 },
11188             {  0, -1 },
11189             {  0,  0 }, { 0, 0 }, { 0, 0 },
11190             {  0, +1 }
11191           };
11192
11193         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11194         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11195
11196         change->actual_trigger_ce_value = CustomValue[xx][yy];
11197         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11198       }
11199
11200       if (change->can_change && !change_done)
11201       {
11202         ChangeDelay[x][y] = 1;
11203         ChangeEvent[x][y] = trigger_event;
11204
11205         HandleElementChange(x, y, p);
11206
11207         change_done = TRUE;
11208       }
11209       else if (change->has_action)
11210       {
11211         ExecuteCustomElementAction(x, y, element, p);
11212         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11213       }
11214     }
11215   }
11216
11217   RECURSION_LOOP_DETECTION_END();
11218
11219   return change_done;
11220 }
11221
11222 static void PlayPlayerSound(struct PlayerInfo *player)
11223 {
11224   int jx = player->jx, jy = player->jy;
11225   int sound_element = player->artwork_element;
11226   int last_action = player->last_action_waiting;
11227   int action = player->action_waiting;
11228
11229   if (player->is_waiting)
11230   {
11231     if (action != last_action)
11232       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11233     else
11234       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11235   }
11236   else
11237   {
11238     if (action != last_action)
11239       StopSound(element_info[sound_element].sound[last_action]);
11240
11241     if (last_action == ACTION_SLEEPING)
11242       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11243   }
11244 }
11245
11246 static void PlayAllPlayersSound(void)
11247 {
11248   int i;
11249
11250   for (i = 0; i < MAX_PLAYERS; i++)
11251     if (stored_player[i].active)
11252       PlayPlayerSound(&stored_player[i]);
11253 }
11254
11255 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11256 {
11257   boolean last_waiting = player->is_waiting;
11258   int move_dir = player->MovDir;
11259
11260   player->dir_waiting = move_dir;
11261   player->last_action_waiting = player->action_waiting;
11262
11263   if (is_waiting)
11264   {
11265     if (!last_waiting)          // not waiting -> waiting
11266     {
11267       player->is_waiting = TRUE;
11268
11269       player->frame_counter_bored =
11270         FrameCounter +
11271         game.player_boring_delay_fixed +
11272         GetSimpleRandom(game.player_boring_delay_random);
11273       player->frame_counter_sleeping =
11274         FrameCounter +
11275         game.player_sleeping_delay_fixed +
11276         GetSimpleRandom(game.player_sleeping_delay_random);
11277
11278       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11279     }
11280
11281     if (game.player_sleeping_delay_fixed +
11282         game.player_sleeping_delay_random > 0 &&
11283         player->anim_delay_counter == 0 &&
11284         player->post_delay_counter == 0 &&
11285         FrameCounter >= player->frame_counter_sleeping)
11286       player->is_sleeping = TRUE;
11287     else if (game.player_boring_delay_fixed +
11288              game.player_boring_delay_random > 0 &&
11289              FrameCounter >= player->frame_counter_bored)
11290       player->is_bored = TRUE;
11291
11292     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11293                               player->is_bored ? ACTION_BORING :
11294                               ACTION_WAITING);
11295
11296     if (player->is_sleeping && player->use_murphy)
11297     {
11298       // special case for sleeping Murphy when leaning against non-free tile
11299
11300       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11301           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11302            !IS_MOVING(player->jx - 1, player->jy)))
11303         move_dir = MV_LEFT;
11304       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11305                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11306                 !IS_MOVING(player->jx + 1, player->jy)))
11307         move_dir = MV_RIGHT;
11308       else
11309         player->is_sleeping = FALSE;
11310
11311       player->dir_waiting = move_dir;
11312     }
11313
11314     if (player->is_sleeping)
11315     {
11316       if (player->num_special_action_sleeping > 0)
11317       {
11318         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11319         {
11320           int last_special_action = player->special_action_sleeping;
11321           int num_special_action = player->num_special_action_sleeping;
11322           int special_action =
11323             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11324              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11325              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11326              last_special_action + 1 : ACTION_SLEEPING);
11327           int special_graphic =
11328             el_act_dir2img(player->artwork_element, special_action, move_dir);
11329
11330           player->anim_delay_counter =
11331             graphic_info[special_graphic].anim_delay_fixed +
11332             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11333           player->post_delay_counter =
11334             graphic_info[special_graphic].post_delay_fixed +
11335             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11336
11337           player->special_action_sleeping = special_action;
11338         }
11339
11340         if (player->anim_delay_counter > 0)
11341         {
11342           player->action_waiting = player->special_action_sleeping;
11343           player->anim_delay_counter--;
11344         }
11345         else if (player->post_delay_counter > 0)
11346         {
11347           player->post_delay_counter--;
11348         }
11349       }
11350     }
11351     else if (player->is_bored)
11352     {
11353       if (player->num_special_action_bored > 0)
11354       {
11355         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11356         {
11357           int special_action =
11358             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11359           int special_graphic =
11360             el_act_dir2img(player->artwork_element, special_action, move_dir);
11361
11362           player->anim_delay_counter =
11363             graphic_info[special_graphic].anim_delay_fixed +
11364             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11365           player->post_delay_counter =
11366             graphic_info[special_graphic].post_delay_fixed +
11367             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11368
11369           player->special_action_bored = special_action;
11370         }
11371
11372         if (player->anim_delay_counter > 0)
11373         {
11374           player->action_waiting = player->special_action_bored;
11375           player->anim_delay_counter--;
11376         }
11377         else if (player->post_delay_counter > 0)
11378         {
11379           player->post_delay_counter--;
11380         }
11381       }
11382     }
11383   }
11384   else if (last_waiting)        // waiting -> not waiting
11385   {
11386     player->is_waiting = FALSE;
11387     player->is_bored = FALSE;
11388     player->is_sleeping = FALSE;
11389
11390     player->frame_counter_bored = -1;
11391     player->frame_counter_sleeping = -1;
11392
11393     player->anim_delay_counter = 0;
11394     player->post_delay_counter = 0;
11395
11396     player->dir_waiting = player->MovDir;
11397     player->action_waiting = ACTION_DEFAULT;
11398
11399     player->special_action_bored = ACTION_DEFAULT;
11400     player->special_action_sleeping = ACTION_DEFAULT;
11401   }
11402 }
11403
11404 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11405 {
11406   if ((!player->is_moving  && player->was_moving) ||
11407       (player->MovPos == 0 && player->was_moving) ||
11408       (player->is_snapping && !player->was_snapping) ||
11409       (player->is_dropping && !player->was_dropping))
11410   {
11411     if (!CheckSaveEngineSnapshotToList())
11412       return;
11413
11414     player->was_moving = FALSE;
11415     player->was_snapping = TRUE;
11416     player->was_dropping = TRUE;
11417   }
11418   else
11419   {
11420     if (player->is_moving)
11421       player->was_moving = TRUE;
11422
11423     if (!player->is_snapping)
11424       player->was_snapping = FALSE;
11425
11426     if (!player->is_dropping)
11427       player->was_dropping = FALSE;
11428   }
11429
11430   static struct MouseActionInfo mouse_action_last = { 0 };
11431   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11432   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11433
11434   if (new_released)
11435     CheckSaveEngineSnapshotToList();
11436
11437   mouse_action_last = mouse_action;
11438 }
11439
11440 static void CheckSingleStepMode(struct PlayerInfo *player)
11441 {
11442   if (tape.single_step && tape.recording && !tape.pausing)
11443   {
11444     // as it is called "single step mode", just return to pause mode when the
11445     // player stopped moving after one tile (or never starts moving at all)
11446     // (reverse logic needed here in case single step mode used in team mode)
11447     if (player->is_moving ||
11448         player->is_pushing ||
11449         player->is_dropping_pressed ||
11450         player->effective_mouse_action.button)
11451       game.enter_single_step_mode = FALSE;
11452   }
11453
11454   CheckSaveEngineSnapshot(player);
11455 }
11456
11457 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11458 {
11459   int left      = player_action & JOY_LEFT;
11460   int right     = player_action & JOY_RIGHT;
11461   int up        = player_action & JOY_UP;
11462   int down      = player_action & JOY_DOWN;
11463   int button1   = player_action & JOY_BUTTON_1;
11464   int button2   = player_action & JOY_BUTTON_2;
11465   int dx        = (left ? -1 : right ? 1 : 0);
11466   int dy        = (up   ? -1 : down  ? 1 : 0);
11467
11468   if (!player->active || tape.pausing)
11469     return 0;
11470
11471   if (player_action)
11472   {
11473     if (button1)
11474       SnapField(player, dx, dy);
11475     else
11476     {
11477       if (button2)
11478         DropElement(player);
11479
11480       MovePlayer(player, dx, dy);
11481     }
11482
11483     CheckSingleStepMode(player);
11484
11485     SetPlayerWaiting(player, FALSE);
11486
11487     return player_action;
11488   }
11489   else
11490   {
11491     // no actions for this player (no input at player's configured device)
11492
11493     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11494     SnapField(player, 0, 0);
11495     CheckGravityMovementWhenNotMoving(player);
11496
11497     if (player->MovPos == 0)
11498       SetPlayerWaiting(player, TRUE);
11499
11500     if (player->MovPos == 0)    // needed for tape.playing
11501       player->is_moving = FALSE;
11502
11503     player->is_dropping = FALSE;
11504     player->is_dropping_pressed = FALSE;
11505     player->drop_pressed_delay = 0;
11506
11507     CheckSingleStepMode(player);
11508
11509     return 0;
11510   }
11511 }
11512
11513 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11514                                          byte *tape_action)
11515 {
11516   if (!tape.use_mouse_actions)
11517     return;
11518
11519   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11520   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11521   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11522 }
11523
11524 static void SetTapeActionFromMouseAction(byte *tape_action,
11525                                          struct MouseActionInfo *mouse_action)
11526 {
11527   if (!tape.use_mouse_actions)
11528     return;
11529
11530   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11531   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11532   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11533 }
11534
11535 static void CheckLevelSolved(void)
11536 {
11537   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11538   {
11539     if (game_em.level_solved &&
11540         !game_em.game_over)                             // game won
11541     {
11542       LevelSolved();
11543
11544       game_em.game_over = TRUE;
11545
11546       game.all_players_gone = TRUE;
11547     }
11548
11549     if (game_em.game_over)                              // game lost
11550       game.all_players_gone = TRUE;
11551   }
11552   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11553   {
11554     if (game_sp.level_solved &&
11555         !game_sp.game_over)                             // game won
11556     {
11557       LevelSolved();
11558
11559       game_sp.game_over = TRUE;
11560
11561       game.all_players_gone = TRUE;
11562     }
11563
11564     if (game_sp.game_over)                              // game lost
11565       game.all_players_gone = TRUE;
11566   }
11567   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11568   {
11569     if (game_mm.level_solved &&
11570         !game_mm.game_over)                             // game won
11571     {
11572       LevelSolved();
11573
11574       game_mm.game_over = TRUE;
11575
11576       game.all_players_gone = TRUE;
11577     }
11578
11579     if (game_mm.game_over)                              // game lost
11580       game.all_players_gone = TRUE;
11581   }
11582 }
11583
11584 static void CheckLevelTime_StepCounter(void)
11585 {
11586   int i;
11587
11588   TimePlayed++;
11589
11590   if (TimeLeft > 0)
11591   {
11592     TimeLeft--;
11593
11594     if (TimeLeft <= 10 && game.time_limit && !game.LevelSolved)
11595       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11596
11597     game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11598
11599     DisplayGameControlValues();
11600
11601     if (!TimeLeft && game.time_limit && !game.LevelSolved)
11602       for (i = 0; i < MAX_PLAYERS; i++)
11603         KillPlayer(&stored_player[i]);
11604   }
11605   else if (game.no_level_time_limit && !game.all_players_gone)
11606   {
11607     game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11608
11609     DisplayGameControlValues();
11610   }
11611 }
11612
11613 static void CheckLevelTime(void)
11614 {
11615   int i;
11616
11617   if (TimeFrames >= FRAMES_PER_SECOND)
11618   {
11619     TimeFrames = 0;
11620     TapeTime++;
11621
11622     for (i = 0; i < MAX_PLAYERS; i++)
11623     {
11624       struct PlayerInfo *player = &stored_player[i];
11625
11626       if (SHIELD_ON(player))
11627       {
11628         player->shield_normal_time_left--;
11629
11630         if (player->shield_deadly_time_left > 0)
11631           player->shield_deadly_time_left--;
11632       }
11633     }
11634
11635     if (!game.LevelSolved && !level.use_step_counter)
11636     {
11637       TimePlayed++;
11638
11639       if (TimeLeft > 0)
11640       {
11641         TimeLeft--;
11642
11643         if (TimeLeft <= 10 && game.time_limit)
11644           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11645
11646         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11647            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11648
11649         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11650
11651         if (!TimeLeft && game.time_limit)
11652         {
11653           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11654             game_em.lev->killed_out_of_time = TRUE;
11655           else
11656             for (i = 0; i < MAX_PLAYERS; i++)
11657               KillPlayer(&stored_player[i]);
11658         }
11659       }
11660       else if (game.no_level_time_limit && !game.all_players_gone)
11661       {
11662         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11663       }
11664
11665       game_em.lev->time = (game.no_level_time_limit ? TimePlayed : TimeLeft);
11666     }
11667
11668     if (tape.recording || tape.playing)
11669       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11670   }
11671
11672   if (tape.recording || tape.playing)
11673     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11674
11675   UpdateAndDisplayGameControlValues();
11676 }
11677
11678 void AdvanceFrameAndPlayerCounters(int player_nr)
11679 {
11680   int i;
11681
11682   // advance frame counters (global frame counter and time frame counter)
11683   FrameCounter++;
11684   TimeFrames++;
11685
11686   // advance player counters (counters for move delay, move animation etc.)
11687   for (i = 0; i < MAX_PLAYERS; i++)
11688   {
11689     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11690     int move_delay_value = stored_player[i].move_delay_value;
11691     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11692
11693     if (!advance_player_counters)       // not all players may be affected
11694       continue;
11695
11696     if (move_frames == 0)       // less than one move per game frame
11697     {
11698       int stepsize = TILEX / move_delay_value;
11699       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11700       int count = (stored_player[i].is_moving ?
11701                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11702
11703       if (count % delay == 0)
11704         move_frames = 1;
11705     }
11706
11707     stored_player[i].Frame += move_frames;
11708
11709     if (stored_player[i].MovPos != 0)
11710       stored_player[i].StepFrame += move_frames;
11711
11712     if (stored_player[i].move_delay > 0)
11713       stored_player[i].move_delay--;
11714
11715     // due to bugs in previous versions, counter must count up, not down
11716     if (stored_player[i].push_delay != -1)
11717       stored_player[i].push_delay++;
11718
11719     if (stored_player[i].drop_delay > 0)
11720       stored_player[i].drop_delay--;
11721
11722     if (stored_player[i].is_dropping_pressed)
11723       stored_player[i].drop_pressed_delay++;
11724   }
11725 }
11726
11727 void AdvanceFrameCounter(void)
11728 {
11729   FrameCounter++;
11730 }
11731
11732 void AdvanceGfxFrame(void)
11733 {
11734   int x, y;
11735
11736   SCAN_PLAYFIELD(x, y)
11737   {
11738     GfxFrame[x][y]++;
11739   }
11740 }
11741
11742 static void HandleMouseAction(struct MouseActionInfo *mouse_action,
11743                               struct MouseActionInfo *mouse_action_last)
11744 {
11745   if (mouse_action->button)
11746   {
11747     int new_button = (mouse_action->button && mouse_action_last->button == 0);
11748     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action->button);
11749     int x = mouse_action->lx;
11750     int y = mouse_action->ly;
11751     int element = Tile[x][y];
11752
11753     if (new_button)
11754     {
11755       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
11756       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
11757                                          ch_button);
11758     }
11759
11760     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
11761     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
11762                                        ch_button);
11763
11764     if (level.use_step_counter)
11765     {
11766       boolean counted_click = FALSE;
11767
11768       // element clicked that can change when clicked/pressed
11769       if (CAN_CHANGE_OR_HAS_ACTION(element) &&
11770           (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
11771            HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE)))
11772         counted_click = TRUE;
11773
11774       // element clicked that can trigger change when clicked/pressed
11775       if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] ||
11776           trigger_events[element][CE_MOUSE_PRESSED_ON_X])
11777         counted_click = TRUE;
11778
11779       if (new_button && counted_click)
11780         CheckLevelTime_StepCounter();
11781     }
11782   }
11783 }
11784
11785 void StartGameActions(boolean init_network_game, boolean record_tape,
11786                       int random_seed)
11787 {
11788   unsigned int new_random_seed = InitRND(random_seed);
11789
11790   if (record_tape)
11791     TapeStartRecording(new_random_seed);
11792
11793   if (setup.auto_pause_on_start && !tape.pausing)
11794     TapeTogglePause(TAPE_TOGGLE_MANUAL);
11795
11796   if (init_network_game)
11797   {
11798     SendToServer_LevelFile();
11799     SendToServer_StartPlaying();
11800
11801     return;
11802   }
11803
11804   InitGame();
11805 }
11806
11807 static void GameActionsExt(void)
11808 {
11809 #if 0
11810   static unsigned int game_frame_delay = 0;
11811 #endif
11812   unsigned int game_frame_delay_value;
11813   byte *recorded_player_action;
11814   byte summarized_player_action = 0;
11815   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11816   int i;
11817
11818   // detect endless loops, caused by custom element programming
11819   if (recursion_loop_detected && recursion_loop_depth == 0)
11820   {
11821     char *message = getStringCat3("Internal Error! Element ",
11822                                   EL_NAME(recursion_loop_element),
11823                                   " caused endless loop! Quit the game?");
11824
11825     Warn("element '%s' caused endless loop in game engine",
11826          EL_NAME(recursion_loop_element));
11827
11828     RequestQuitGameExt(program.headless, level_editor_test_game, message);
11829
11830     recursion_loop_detected = FALSE;    // if game should be continued
11831
11832     free(message);
11833
11834     return;
11835   }
11836
11837   if (game.restart_level)
11838     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11839
11840   CheckLevelSolved();
11841
11842   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11843     GameWon();
11844
11845   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11846     TapeStop();
11847
11848   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11849     return;
11850
11851   game_frame_delay_value =
11852     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11853
11854   if (tape.playing && tape.warp_forward && !tape.pausing)
11855     game_frame_delay_value = 0;
11856
11857   SetVideoFrameDelay(game_frame_delay_value);
11858
11859   // (de)activate virtual buttons depending on current game status
11860   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11861   {
11862     if (game.all_players_gone)  // if no players there to be controlled anymore
11863       SetOverlayActive(FALSE);
11864     else if (!tape.playing)     // if game continues after tape stopped playing
11865       SetOverlayActive(TRUE);
11866   }
11867
11868 #if 0
11869 #if 0
11870   // ---------- main game synchronization point ----------
11871
11872   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11873
11874   Debug("game:playing:skip", "skip == %d", skip);
11875
11876 #else
11877   // ---------- main game synchronization point ----------
11878
11879   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11880 #endif
11881 #endif
11882
11883   if (network_playing && !network_player_action_received)
11884   {
11885     // try to get network player actions in time
11886
11887     // last chance to get network player actions without main loop delay
11888     HandleNetworking();
11889
11890     // game was quit by network peer
11891     if (game_status != GAME_MODE_PLAYING)
11892       return;
11893
11894     // check if network player actions still missing and game still running
11895     if (!network_player_action_received && !checkGameEnded())
11896       return;           // failed to get network player actions in time
11897
11898     // do not yet reset "network_player_action_received" (for tape.pausing)
11899   }
11900
11901   if (tape.pausing)
11902     return;
11903
11904   // at this point we know that we really continue executing the game
11905
11906   network_player_action_received = FALSE;
11907
11908   // when playing tape, read previously recorded player input from tape data
11909   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11910
11911   local_player->effective_mouse_action = local_player->mouse_action;
11912
11913   if (recorded_player_action != NULL)
11914     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11915                                  recorded_player_action);
11916
11917   // TapePlayAction() may return NULL when toggling to "pause before death"
11918   if (tape.pausing)
11919     return;
11920
11921   if (tape.set_centered_player)
11922   {
11923     game.centered_player_nr_next = tape.centered_player_nr_next;
11924     game.set_centered_player = TRUE;
11925   }
11926
11927   for (i = 0; i < MAX_PLAYERS; i++)
11928   {
11929     summarized_player_action |= stored_player[i].action;
11930
11931     if (!network_playing && (game.team_mode || tape.playing))
11932       stored_player[i].effective_action = stored_player[i].action;
11933   }
11934
11935   if (network_playing && !checkGameEnded())
11936     SendToServer_MovePlayer(summarized_player_action);
11937
11938   // summarize all actions at local players mapped input device position
11939   // (this allows using different input devices in single player mode)
11940   if (!network.enabled && !game.team_mode)
11941     stored_player[map_player_action[local_player->index_nr]].effective_action =
11942       summarized_player_action;
11943
11944   // summarize all actions at centered player in local team mode
11945   if (tape.recording &&
11946       setup.team_mode && !network.enabled &&
11947       setup.input_on_focus &&
11948       game.centered_player_nr != -1)
11949   {
11950     for (i = 0; i < MAX_PLAYERS; i++)
11951       stored_player[map_player_action[i]].effective_action =
11952         (i == game.centered_player_nr ? summarized_player_action : 0);
11953   }
11954
11955   if (recorded_player_action != NULL)
11956     for (i = 0; i < MAX_PLAYERS; i++)
11957       stored_player[i].effective_action = recorded_player_action[i];
11958
11959   for (i = 0; i < MAX_PLAYERS; i++)
11960   {
11961     tape_action[i] = stored_player[i].effective_action;
11962
11963     /* (this may happen in the RND game engine if a player was not present on
11964        the playfield on level start, but appeared later from a custom element */
11965     if (setup.team_mode &&
11966         tape.recording &&
11967         tape_action[i] &&
11968         !tape.player_participates[i])
11969       tape.player_participates[i] = TRUE;
11970   }
11971
11972   SetTapeActionFromMouseAction(tape_action,
11973                                &local_player->effective_mouse_action);
11974
11975   // only record actions from input devices, but not programmed actions
11976   if (tape.recording)
11977     TapeRecordAction(tape_action);
11978
11979   // remember if game was played (especially after tape stopped playing)
11980   if (!tape.playing && summarized_player_action && !checkGameFailed())
11981     game.GamePlayed = TRUE;
11982
11983 #if USE_NEW_PLAYER_ASSIGNMENTS
11984   // !!! also map player actions in single player mode !!!
11985   // if (game.team_mode)
11986   if (1)
11987   {
11988     byte mapped_action[MAX_PLAYERS];
11989
11990 #if DEBUG_PLAYER_ACTIONS
11991     for (i = 0; i < MAX_PLAYERS; i++)
11992       DebugContinued("", "%d, ", stored_player[i].effective_action);
11993 #endif
11994
11995     for (i = 0; i < MAX_PLAYERS; i++)
11996       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11997
11998     for (i = 0; i < MAX_PLAYERS; i++)
11999       stored_player[i].effective_action = mapped_action[i];
12000
12001 #if DEBUG_PLAYER_ACTIONS
12002     DebugContinued("", "=> ");
12003     for (i = 0; i < MAX_PLAYERS; i++)
12004       DebugContinued("", "%d, ", stored_player[i].effective_action);
12005     DebugContinued("game:playing:player", "\n");
12006 #endif
12007   }
12008 #if DEBUG_PLAYER_ACTIONS
12009   else
12010   {
12011     for (i = 0; i < MAX_PLAYERS; i++)
12012       DebugContinued("", "%d, ", stored_player[i].effective_action);
12013     DebugContinued("game:playing:player", "\n");
12014   }
12015 #endif
12016 #endif
12017
12018   for (i = 0; i < MAX_PLAYERS; i++)
12019   {
12020     // allow engine snapshot in case of changed movement attempt
12021     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
12022         (stored_player[i].effective_action & KEY_MOTION))
12023       game.snapshot.changed_action = TRUE;
12024
12025     // allow engine snapshot in case of snapping/dropping attempt
12026     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
12027         (stored_player[i].effective_action & KEY_BUTTON) != 0)
12028       game.snapshot.changed_action = TRUE;
12029
12030     game.snapshot.last_action[i] = stored_player[i].effective_action;
12031   }
12032
12033   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12034   {
12035     GameActions_EM_Main();
12036   }
12037   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12038   {
12039     GameActions_SP_Main();
12040   }
12041   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
12042   {
12043     GameActions_MM_Main();
12044   }
12045   else
12046   {
12047     GameActions_RND_Main();
12048   }
12049
12050   BlitScreenToBitmap(backbuffer);
12051
12052   CheckLevelSolved();
12053   CheckLevelTime();
12054
12055   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
12056
12057   if (global.show_frames_per_second)
12058   {
12059     static unsigned int fps_counter = 0;
12060     static int fps_frames = 0;
12061     unsigned int fps_delay_ms = Counter() - fps_counter;
12062
12063     fps_frames++;
12064
12065     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
12066     {
12067       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12068
12069       fps_frames = 0;
12070       fps_counter = Counter();
12071
12072       // always draw FPS to screen after FPS value was updated
12073       redraw_mask |= REDRAW_FPS;
12074     }
12075
12076     // only draw FPS if no screen areas are deactivated (invisible warp mode)
12077     if (GetDrawDeactivationMask() == REDRAW_NONE)
12078       redraw_mask |= REDRAW_FPS;
12079   }
12080 }
12081
12082 static void GameActions_CheckSaveEngineSnapshot(void)
12083 {
12084   if (!game.snapshot.save_snapshot)
12085     return;
12086
12087   // clear flag for saving snapshot _before_ saving snapshot
12088   game.snapshot.save_snapshot = FALSE;
12089
12090   SaveEngineSnapshotToList();
12091 }
12092
12093 void GameActions(void)
12094 {
12095   GameActionsExt();
12096
12097   GameActions_CheckSaveEngineSnapshot();
12098 }
12099
12100 void GameActions_EM_Main(void)
12101 {
12102   byte effective_action[MAX_PLAYERS];
12103   int i;
12104
12105   for (i = 0; i < MAX_PLAYERS; i++)
12106     effective_action[i] = stored_player[i].effective_action;
12107
12108   GameActions_EM(effective_action);
12109 }
12110
12111 void GameActions_SP_Main(void)
12112 {
12113   byte effective_action[MAX_PLAYERS];
12114   int i;
12115
12116   for (i = 0; i < MAX_PLAYERS; i++)
12117     effective_action[i] = stored_player[i].effective_action;
12118
12119   GameActions_SP(effective_action);
12120
12121   for (i = 0; i < MAX_PLAYERS; i++)
12122   {
12123     if (stored_player[i].force_dropping)
12124       stored_player[i].action |= KEY_BUTTON_DROP;
12125
12126     stored_player[i].force_dropping = FALSE;
12127   }
12128 }
12129
12130 void GameActions_MM_Main(void)
12131 {
12132   AdvanceGfxFrame();
12133
12134   GameActions_MM(local_player->effective_mouse_action);
12135 }
12136
12137 void GameActions_RND_Main(void)
12138 {
12139   GameActions_RND();
12140 }
12141
12142 void GameActions_RND(void)
12143 {
12144   static struct MouseActionInfo mouse_action_last = { 0 };
12145   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12146   int magic_wall_x = 0, magic_wall_y = 0;
12147   int i, x, y, element, graphic, last_gfx_frame;
12148
12149   InitPlayfieldScanModeVars();
12150
12151   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12152   {
12153     SCAN_PLAYFIELD(x, y)
12154     {
12155       ChangeCount[x][y] = 0;
12156       ChangeEvent[x][y] = -1;
12157     }
12158   }
12159
12160   if (game.set_centered_player)
12161   {
12162     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12163
12164     // switching to "all players" only possible if all players fit to screen
12165     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12166     {
12167       game.centered_player_nr_next = game.centered_player_nr;
12168       game.set_centered_player = FALSE;
12169     }
12170
12171     // do not switch focus to non-existing (or non-active) player
12172     if (game.centered_player_nr_next >= 0 &&
12173         !stored_player[game.centered_player_nr_next].active)
12174     {
12175       game.centered_player_nr_next = game.centered_player_nr;
12176       game.set_centered_player = FALSE;
12177     }
12178   }
12179
12180   if (game.set_centered_player &&
12181       ScreenMovPos == 0)        // screen currently aligned at tile position
12182   {
12183     int sx, sy;
12184
12185     if (game.centered_player_nr_next == -1)
12186     {
12187       setScreenCenteredToAllPlayers(&sx, &sy);
12188     }
12189     else
12190     {
12191       sx = stored_player[game.centered_player_nr_next].jx;
12192       sy = stored_player[game.centered_player_nr_next].jy;
12193     }
12194
12195     game.centered_player_nr = game.centered_player_nr_next;
12196     game.set_centered_player = FALSE;
12197
12198     DrawRelocateScreen(0, 0, sx, sy, TRUE, setup.quick_switch);
12199     DrawGameDoorValues();
12200   }
12201
12202   // check single step mode (set flag and clear again if any player is active)
12203   game.enter_single_step_mode =
12204     (tape.single_step && tape.recording && !tape.pausing);
12205
12206   for (i = 0; i < MAX_PLAYERS; i++)
12207   {
12208     int actual_player_action = stored_player[i].effective_action;
12209
12210 #if 1
12211     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12212        - rnd_equinox_tetrachloride 048
12213        - rnd_equinox_tetrachloride_ii 096
12214        - rnd_emanuel_schmieg 002
12215        - doctor_sloan_ww 001, 020
12216     */
12217     if (stored_player[i].MovPos == 0)
12218       CheckGravityMovement(&stored_player[i]);
12219 #endif
12220
12221     // overwrite programmed action with tape action
12222     if (stored_player[i].programmed_action)
12223       actual_player_action = stored_player[i].programmed_action;
12224
12225     PlayerActions(&stored_player[i], actual_player_action);
12226
12227     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12228   }
12229
12230   // single step pause mode may already have been toggled by "ScrollPlayer()"
12231   if (game.enter_single_step_mode && !tape.pausing)
12232     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12233
12234   ScrollScreen(NULL, SCROLL_GO_ON);
12235
12236   /* for backwards compatibility, the following code emulates a fixed bug that
12237      occured when pushing elements (causing elements that just made their last
12238      pushing step to already (if possible) make their first falling step in the
12239      same game frame, which is bad); this code is also needed to use the famous
12240      "spring push bug" which is used in older levels and might be wanted to be
12241      used also in newer levels, but in this case the buggy pushing code is only
12242      affecting the "spring" element and no other elements */
12243
12244   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12245   {
12246     for (i = 0; i < MAX_PLAYERS; i++)
12247     {
12248       struct PlayerInfo *player = &stored_player[i];
12249       int x = player->jx;
12250       int y = player->jy;
12251
12252       if (player->active && player->is_pushing && player->is_moving &&
12253           IS_MOVING(x, y) &&
12254           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12255            Tile[x][y] == EL_SPRING))
12256       {
12257         ContinueMoving(x, y);
12258
12259         // continue moving after pushing (this is actually a bug)
12260         if (!IS_MOVING(x, y))
12261           Stop[x][y] = FALSE;
12262       }
12263     }
12264   }
12265
12266   SCAN_PLAYFIELD(x, y)
12267   {
12268     Last[x][y] = Tile[x][y];
12269
12270     ChangeCount[x][y] = 0;
12271     ChangeEvent[x][y] = -1;
12272
12273     // this must be handled before main playfield loop
12274     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12275     {
12276       MovDelay[x][y]--;
12277       if (MovDelay[x][y] <= 0)
12278         RemoveField(x, y);
12279     }
12280
12281     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12282     {
12283       MovDelay[x][y]--;
12284       if (MovDelay[x][y] <= 0)
12285       {
12286         int element = Store[x][y];
12287         int move_direction = MovDir[x][y];
12288         int player_index_bit = Store2[x][y];
12289
12290         Store[x][y] = 0;
12291         Store2[x][y] = 0;
12292
12293         RemoveField(x, y);
12294         TEST_DrawLevelField(x, y);
12295
12296         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12297
12298         if (IS_ENVELOPE(element))
12299           local_player->show_envelope = element;
12300       }
12301     }
12302
12303 #if DEBUG
12304     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12305     {
12306       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12307             x, y);
12308       Debug("game:playing:GameActions_RND", "This should never happen!");
12309
12310       ChangePage[x][y] = -1;
12311     }
12312 #endif
12313
12314     Stop[x][y] = FALSE;
12315     if (WasJustMoving[x][y] > 0)
12316       WasJustMoving[x][y]--;
12317     if (WasJustFalling[x][y] > 0)
12318       WasJustFalling[x][y]--;
12319     if (CheckCollision[x][y] > 0)
12320       CheckCollision[x][y]--;
12321     if (CheckImpact[x][y] > 0)
12322       CheckImpact[x][y]--;
12323
12324     GfxFrame[x][y]++;
12325
12326     /* reset finished pushing action (not done in ContinueMoving() to allow
12327        continuous pushing animation for elements with zero push delay) */
12328     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12329     {
12330       ResetGfxAnimation(x, y);
12331       TEST_DrawLevelField(x, y);
12332     }
12333
12334 #if DEBUG
12335     if (IS_BLOCKED(x, y))
12336     {
12337       int oldx, oldy;
12338
12339       Blocked2Moving(x, y, &oldx, &oldy);
12340       if (!IS_MOVING(oldx, oldy))
12341       {
12342         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12343         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12344         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12345         Debug("game:playing:GameActions_RND", "This should never happen!");
12346       }
12347     }
12348 #endif
12349   }
12350
12351   HandleMouseAction(&mouse_action, &mouse_action_last);
12352
12353   SCAN_PLAYFIELD(x, y)
12354   {
12355     element = Tile[x][y];
12356     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12357     last_gfx_frame = GfxFrame[x][y];
12358
12359     if (element == EL_EMPTY)
12360       graphic = el2img(GfxElementEmpty[x][y]);
12361
12362     ResetGfxFrame(x, y);
12363
12364     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12365       DrawLevelGraphicAnimation(x, y, graphic);
12366
12367     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12368         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12369       ResetRandomAnimationValue(x, y);
12370
12371     SetRandomAnimationValue(x, y);
12372
12373     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12374
12375     if (IS_INACTIVE(element))
12376     {
12377       if (IS_ANIMATED(graphic))
12378         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12379
12380       continue;
12381     }
12382
12383     // this may take place after moving, so 'element' may have changed
12384     if (IS_CHANGING(x, y) &&
12385         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12386     {
12387       int page = element_info[element].event_page_nr[CE_DELAY];
12388
12389       HandleElementChange(x, y, page);
12390
12391       element = Tile[x][y];
12392       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12393     }
12394
12395     CheckNextToConditions(x, y);
12396
12397     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12398     {
12399       StartMoving(x, y);
12400
12401       element = Tile[x][y];
12402       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12403
12404       if (IS_ANIMATED(graphic) &&
12405           !IS_MOVING(x, y) &&
12406           !Stop[x][y])
12407         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12408
12409       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12410         TEST_DrawTwinkleOnField(x, y);
12411     }
12412     else if (element == EL_ACID)
12413     {
12414       if (!Stop[x][y])
12415         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12416     }
12417     else if ((element == EL_EXIT_OPEN ||
12418               element == EL_EM_EXIT_OPEN ||
12419               element == EL_SP_EXIT_OPEN ||
12420               element == EL_STEEL_EXIT_OPEN ||
12421               element == EL_EM_STEEL_EXIT_OPEN ||
12422               element == EL_SP_TERMINAL ||
12423               element == EL_SP_TERMINAL_ACTIVE ||
12424               element == EL_EXTRA_TIME ||
12425               element == EL_SHIELD_NORMAL ||
12426               element == EL_SHIELD_DEADLY) &&
12427              IS_ANIMATED(graphic))
12428       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12429     else if (IS_MOVING(x, y))
12430       ContinueMoving(x, y);
12431     else if (IS_ACTIVE_BOMB(element))
12432       CheckDynamite(x, y);
12433     else if (element == EL_AMOEBA_GROWING)
12434       AmoebaGrowing(x, y);
12435     else if (element == EL_AMOEBA_SHRINKING)
12436       AmoebaShrinking(x, y);
12437
12438 #if !USE_NEW_AMOEBA_CODE
12439     else if (IS_AMOEBALIVE(element))
12440       AmoebaReproduce(x, y);
12441 #endif
12442
12443     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12444       Life(x, y);
12445     else if (element == EL_EXIT_CLOSED)
12446       CheckExit(x, y);
12447     else if (element == EL_EM_EXIT_CLOSED)
12448       CheckExitEM(x, y);
12449     else if (element == EL_STEEL_EXIT_CLOSED)
12450       CheckExitSteel(x, y);
12451     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12452       CheckExitSteelEM(x, y);
12453     else if (element == EL_SP_EXIT_CLOSED)
12454       CheckExitSP(x, y);
12455     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12456              element == EL_EXPANDABLE_STEELWALL_GROWING)
12457       WallGrowing(x, y);
12458     else if (element == EL_EXPANDABLE_WALL ||
12459              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12460              element == EL_EXPANDABLE_WALL_VERTICAL ||
12461              element == EL_EXPANDABLE_WALL_ANY ||
12462              element == EL_BD_EXPANDABLE_WALL ||
12463              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12464              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12465              element == EL_EXPANDABLE_STEELWALL_ANY)
12466       CheckWallGrowing(x, y);
12467     else if (element == EL_FLAMES)
12468       CheckForDragon(x, y);
12469     else if (element == EL_EXPLOSION)
12470       ; // drawing of correct explosion animation is handled separately
12471     else if (element == EL_ELEMENT_SNAPPING ||
12472              element == EL_DIAGONAL_SHRINKING ||
12473              element == EL_DIAGONAL_GROWING)
12474     {
12475       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y], GfxDir[x][y]);
12476
12477       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12478     }
12479     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12480       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12481
12482     if (IS_BELT_ACTIVE(element))
12483       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12484
12485     if (game.magic_wall_active)
12486     {
12487       int jx = local_player->jx, jy = local_player->jy;
12488
12489       // play the element sound at the position nearest to the player
12490       if ((element == EL_MAGIC_WALL_FULL ||
12491            element == EL_MAGIC_WALL_ACTIVE ||
12492            element == EL_MAGIC_WALL_EMPTYING ||
12493            element == EL_BD_MAGIC_WALL_FULL ||
12494            element == EL_BD_MAGIC_WALL_ACTIVE ||
12495            element == EL_BD_MAGIC_WALL_EMPTYING ||
12496            element == EL_DC_MAGIC_WALL_FULL ||
12497            element == EL_DC_MAGIC_WALL_ACTIVE ||
12498            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12499           ABS(x - jx) + ABS(y - jy) <
12500           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12501       {
12502         magic_wall_x = x;
12503         magic_wall_y = y;
12504       }
12505     }
12506   }
12507
12508 #if USE_NEW_AMOEBA_CODE
12509   // new experimental amoeba growth stuff
12510   if (!(FrameCounter % 8))
12511   {
12512     static unsigned int random = 1684108901;
12513
12514     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12515     {
12516       x = RND(lev_fieldx);
12517       y = RND(lev_fieldy);
12518       element = Tile[x][y];
12519
12520       if (!IS_PLAYER(x, y) &&
12521           (element == EL_EMPTY ||
12522            CAN_GROW_INTO(element) ||
12523            element == EL_QUICKSAND_EMPTY ||
12524            element == EL_QUICKSAND_FAST_EMPTY ||
12525            element == EL_ACID_SPLASH_LEFT ||
12526            element == EL_ACID_SPLASH_RIGHT))
12527       {
12528         if ((IN_LEV_FIELD(x, y - 1) && Tile[x][y - 1] == EL_AMOEBA_WET) ||
12529             (IN_LEV_FIELD(x - 1, y) && Tile[x - 1][y] == EL_AMOEBA_WET) ||
12530             (IN_LEV_FIELD(x + 1, y) && Tile[x + 1][y] == EL_AMOEBA_WET) ||
12531             (IN_LEV_FIELD(x, y + 1) && Tile[x][y + 1] == EL_AMOEBA_WET))
12532           Tile[x][y] = EL_AMOEBA_DROP;
12533       }
12534
12535       random = random * 129 + 1;
12536     }
12537   }
12538 #endif
12539
12540   game.explosions_delayed = FALSE;
12541
12542   SCAN_PLAYFIELD(x, y)
12543   {
12544     element = Tile[x][y];
12545
12546     if (ExplodeField[x][y])
12547       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12548     else if (element == EL_EXPLOSION)
12549       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12550
12551     ExplodeField[x][y] = EX_TYPE_NONE;
12552   }
12553
12554   game.explosions_delayed = TRUE;
12555
12556   if (game.magic_wall_active)
12557   {
12558     if (!(game.magic_wall_time_left % 4))
12559     {
12560       int element = Tile[magic_wall_x][magic_wall_y];
12561
12562       if (element == EL_BD_MAGIC_WALL_FULL ||
12563           element == EL_BD_MAGIC_WALL_ACTIVE ||
12564           element == EL_BD_MAGIC_WALL_EMPTYING)
12565         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12566       else if (element == EL_DC_MAGIC_WALL_FULL ||
12567                element == EL_DC_MAGIC_WALL_ACTIVE ||
12568                element == EL_DC_MAGIC_WALL_EMPTYING)
12569         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12570       else
12571         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12572     }
12573
12574     if (game.magic_wall_time_left > 0)
12575     {
12576       game.magic_wall_time_left--;
12577
12578       if (!game.magic_wall_time_left)
12579       {
12580         SCAN_PLAYFIELD(x, y)
12581         {
12582           element = Tile[x][y];
12583
12584           if (element == EL_MAGIC_WALL_ACTIVE ||
12585               element == EL_MAGIC_WALL_FULL)
12586           {
12587             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12588             TEST_DrawLevelField(x, y);
12589           }
12590           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12591                    element == EL_BD_MAGIC_WALL_FULL)
12592           {
12593             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12594             TEST_DrawLevelField(x, y);
12595           }
12596           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12597                    element == EL_DC_MAGIC_WALL_FULL)
12598           {
12599             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12600             TEST_DrawLevelField(x, y);
12601           }
12602         }
12603
12604         game.magic_wall_active = FALSE;
12605       }
12606     }
12607   }
12608
12609   if (game.light_time_left > 0)
12610   {
12611     game.light_time_left--;
12612
12613     if (game.light_time_left == 0)
12614       RedrawAllLightSwitchesAndInvisibleElements();
12615   }
12616
12617   if (game.timegate_time_left > 0)
12618   {
12619     game.timegate_time_left--;
12620
12621     if (game.timegate_time_left == 0)
12622       CloseAllOpenTimegates();
12623   }
12624
12625   if (game.lenses_time_left > 0)
12626   {
12627     game.lenses_time_left--;
12628
12629     if (game.lenses_time_left == 0)
12630       RedrawAllInvisibleElementsForLenses();
12631   }
12632
12633   if (game.magnify_time_left > 0)
12634   {
12635     game.magnify_time_left--;
12636
12637     if (game.magnify_time_left == 0)
12638       RedrawAllInvisibleElementsForMagnifier();
12639   }
12640
12641   for (i = 0; i < MAX_PLAYERS; i++)
12642   {
12643     struct PlayerInfo *player = &stored_player[i];
12644
12645     if (SHIELD_ON(player))
12646     {
12647       if (player->shield_deadly_time_left)
12648         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12649       else if (player->shield_normal_time_left)
12650         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12651     }
12652   }
12653
12654 #if USE_DELAYED_GFX_REDRAW
12655   SCAN_PLAYFIELD(x, y)
12656   {
12657     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12658     {
12659       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12660          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12661
12662       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12663         DrawLevelField(x, y);
12664
12665       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12666         DrawLevelFieldCrumbled(x, y);
12667
12668       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12669         DrawLevelFieldCrumbledNeighbours(x, y);
12670
12671       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12672         DrawTwinkleOnField(x, y);
12673     }
12674
12675     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12676   }
12677 #endif
12678
12679   DrawAllPlayers();
12680   PlayAllPlayersSound();
12681
12682   for (i = 0; i < MAX_PLAYERS; i++)
12683   {
12684     struct PlayerInfo *player = &stored_player[i];
12685
12686     if (player->show_envelope != 0 && (!player->active ||
12687                                        player->MovPos == 0))
12688     {
12689       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12690
12691       player->show_envelope = 0;
12692     }
12693   }
12694
12695   // use random number generator in every frame to make it less predictable
12696   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12697     RND(1);
12698
12699   mouse_action_last = mouse_action;
12700 }
12701
12702 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12703 {
12704   int min_x = x, min_y = y, max_x = x, max_y = y;
12705   int scr_fieldx = getScreenFieldSizeX();
12706   int scr_fieldy = getScreenFieldSizeY();
12707   int i;
12708
12709   for (i = 0; i < MAX_PLAYERS; i++)
12710   {
12711     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12712
12713     if (!stored_player[i].active || &stored_player[i] == player)
12714       continue;
12715
12716     min_x = MIN(min_x, jx);
12717     min_y = MIN(min_y, jy);
12718     max_x = MAX(max_x, jx);
12719     max_y = MAX(max_y, jy);
12720   }
12721
12722   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12723 }
12724
12725 static boolean AllPlayersInVisibleScreen(void)
12726 {
12727   int i;
12728
12729   for (i = 0; i < MAX_PLAYERS; i++)
12730   {
12731     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12732
12733     if (!stored_player[i].active)
12734       continue;
12735
12736     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12737       return FALSE;
12738   }
12739
12740   return TRUE;
12741 }
12742
12743 void ScrollLevel(int dx, int dy)
12744 {
12745   int scroll_offset = 2 * TILEX_VAR;
12746   int x, y;
12747
12748   BlitBitmap(drawto_field, drawto_field,
12749              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12750              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12751              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12752              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12753              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12754              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12755
12756   if (dx != 0)
12757   {
12758     x = (dx == 1 ? BX1 : BX2);
12759     for (y = BY1; y <= BY2; y++)
12760       DrawScreenField(x, y);
12761   }
12762
12763   if (dy != 0)
12764   {
12765     y = (dy == 1 ? BY1 : BY2);
12766     for (x = BX1; x <= BX2; x++)
12767       DrawScreenField(x, y);
12768   }
12769
12770   redraw_mask |= REDRAW_FIELD;
12771 }
12772
12773 static boolean canFallDown(struct PlayerInfo *player)
12774 {
12775   int jx = player->jx, jy = player->jy;
12776
12777   return (IN_LEV_FIELD(jx, jy + 1) &&
12778           (IS_FREE(jx, jy + 1) ||
12779            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12780           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12781           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12782 }
12783
12784 static boolean canPassField(int x, int y, int move_dir)
12785 {
12786   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12787   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12788   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12789   int nextx = x + dx;
12790   int nexty = y + dy;
12791   int element = Tile[x][y];
12792
12793   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12794           !CAN_MOVE(element) &&
12795           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12796           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12797           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12798 }
12799
12800 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12801 {
12802   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12803   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12804   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12805   int newx = x + dx;
12806   int newy = y + dy;
12807
12808   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12809           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12810           (IS_DIGGABLE(Tile[newx][newy]) ||
12811            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12812            canPassField(newx, newy, move_dir)));
12813 }
12814
12815 static void CheckGravityMovement(struct PlayerInfo *player)
12816 {
12817   if (player->gravity && !player->programmed_action)
12818   {
12819     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12820     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12821     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12822     int jx = player->jx, jy = player->jy;
12823     boolean player_is_moving_to_valid_field =
12824       (!player_is_snapping &&
12825        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12826         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12827     boolean player_can_fall_down = canFallDown(player);
12828
12829     if (player_can_fall_down &&
12830         !player_is_moving_to_valid_field)
12831       player->programmed_action = MV_DOWN;
12832   }
12833 }
12834
12835 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12836 {
12837   return CheckGravityMovement(player);
12838
12839   if (player->gravity && !player->programmed_action)
12840   {
12841     int jx = player->jx, jy = player->jy;
12842     boolean field_under_player_is_free =
12843       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12844     boolean player_is_standing_on_valid_field =
12845       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12846        (IS_WALKABLE(Tile[jx][jy]) &&
12847         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12848
12849     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12850       player->programmed_action = MV_DOWN;
12851   }
12852 }
12853
12854 /*
12855   MovePlayerOneStep()
12856   -----------------------------------------------------------------------------
12857   dx, dy:               direction (non-diagonal) to try to move the player to
12858   real_dx, real_dy:     direction as read from input device (can be diagonal)
12859 */
12860
12861 boolean MovePlayerOneStep(struct PlayerInfo *player,
12862                           int dx, int dy, int real_dx, int real_dy)
12863 {
12864   int jx = player->jx, jy = player->jy;
12865   int new_jx = jx + dx, new_jy = jy + dy;
12866   int can_move;
12867   boolean player_can_move = !player->cannot_move;
12868
12869   if (!player->active || (!dx && !dy))
12870     return MP_NO_ACTION;
12871
12872   player->MovDir = (dx < 0 ? MV_LEFT :
12873                     dx > 0 ? MV_RIGHT :
12874                     dy < 0 ? MV_UP :
12875                     dy > 0 ? MV_DOWN :  MV_NONE);
12876
12877   if (!IN_LEV_FIELD(new_jx, new_jy))
12878     return MP_NO_ACTION;
12879
12880   if (!player_can_move)
12881   {
12882     if (player->MovPos == 0)
12883     {
12884       player->is_moving = FALSE;
12885       player->is_digging = FALSE;
12886       player->is_collecting = FALSE;
12887       player->is_snapping = FALSE;
12888       player->is_pushing = FALSE;
12889     }
12890   }
12891
12892   if (!network.enabled && game.centered_player_nr == -1 &&
12893       !AllPlayersInSight(player, new_jx, new_jy))
12894     return MP_NO_ACTION;
12895
12896   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx, real_dy, DF_DIG);
12897   if (can_move != MP_MOVING)
12898     return can_move;
12899
12900   // check if DigField() has caused relocation of the player
12901   if (player->jx != jx || player->jy != jy)
12902     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12903
12904   StorePlayer[jx][jy] = 0;
12905   player->last_jx = jx;
12906   player->last_jy = jy;
12907   player->jx = new_jx;
12908   player->jy = new_jy;
12909   StorePlayer[new_jx][new_jy] = player->element_nr;
12910
12911   if (player->move_delay_value_next != -1)
12912   {
12913     player->move_delay_value = player->move_delay_value_next;
12914     player->move_delay_value_next = -1;
12915   }
12916
12917   player->MovPos =
12918     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12919
12920   player->step_counter++;
12921
12922   PlayerVisit[jx][jy] = FrameCounter;
12923
12924   player->is_moving = TRUE;
12925
12926 #if 1
12927   // should better be called in MovePlayer(), but this breaks some tapes
12928   ScrollPlayer(player, SCROLL_INIT);
12929 #endif
12930
12931   return MP_MOVING;
12932 }
12933
12934 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12935 {
12936   int jx = player->jx, jy = player->jy;
12937   int old_jx = jx, old_jy = jy;
12938   int moved = MP_NO_ACTION;
12939
12940   if (!player->active)
12941     return FALSE;
12942
12943   if (!dx && !dy)
12944   {
12945     if (player->MovPos == 0)
12946     {
12947       player->is_moving = FALSE;
12948       player->is_digging = FALSE;
12949       player->is_collecting = FALSE;
12950       player->is_snapping = FALSE;
12951       player->is_pushing = FALSE;
12952     }
12953
12954     return FALSE;
12955   }
12956
12957   if (player->move_delay > 0)
12958     return FALSE;
12959
12960   player->move_delay = -1;              // set to "uninitialized" value
12961
12962   // store if player is automatically moved to next field
12963   player->is_auto_moving = (player->programmed_action != MV_NONE);
12964
12965   // remove the last programmed player action
12966   player->programmed_action = 0;
12967
12968   if (player->MovPos)
12969   {
12970     // should only happen if pre-1.2 tape recordings are played
12971     // this is only for backward compatibility
12972
12973     int original_move_delay_value = player->move_delay_value;
12974
12975 #if DEBUG
12976     Debug("game:playing:MovePlayer",
12977           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12978           tape.counter);
12979 #endif
12980
12981     // scroll remaining steps with finest movement resolution
12982     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12983
12984     while (player->MovPos)
12985     {
12986       ScrollPlayer(player, SCROLL_GO_ON);
12987       ScrollScreen(NULL, SCROLL_GO_ON);
12988
12989       AdvanceFrameAndPlayerCounters(player->index_nr);
12990
12991       DrawAllPlayers();
12992       BackToFront_WithFrameDelay(0);
12993     }
12994
12995     player->move_delay_value = original_move_delay_value;
12996   }
12997
12998   player->is_active = FALSE;
12999
13000   if (player->last_move_dir & MV_HORIZONTAL)
13001   {
13002     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13003       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13004   }
13005   else
13006   {
13007     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13008       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13009   }
13010
13011   if (!moved && !player->is_active)
13012   {
13013     player->is_moving = FALSE;
13014     player->is_digging = FALSE;
13015     player->is_collecting = FALSE;
13016     player->is_snapping = FALSE;
13017     player->is_pushing = FALSE;
13018   }
13019
13020   jx = player->jx;
13021   jy = player->jy;
13022
13023   if (moved & MP_MOVING && !ScreenMovPos &&
13024       (player->index_nr == game.centered_player_nr ||
13025        game.centered_player_nr == -1))
13026   {
13027     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13028
13029     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13030     {
13031       // actual player has left the screen -- scroll in that direction
13032       if (jx != old_jx)         // player has moved horizontally
13033         scroll_x += (jx - old_jx);
13034       else                      // player has moved vertically
13035         scroll_y += (jy - old_jy);
13036     }
13037     else
13038     {
13039       int offset_raw = game.scroll_delay_value;
13040
13041       if (jx != old_jx)         // player has moved horizontally
13042       {
13043         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
13044         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
13045         int new_scroll_x = jx - MIDPOSX + offset_x;
13046
13047         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
13048             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
13049           scroll_x = new_scroll_x;
13050
13051         // don't scroll over playfield boundaries
13052         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
13053
13054         // don't scroll more than one field at a time
13055         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13056
13057         // don't scroll against the player's moving direction
13058         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13059             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13060           scroll_x = old_scroll_x;
13061       }
13062       else                      // player has moved vertically
13063       {
13064         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
13065         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
13066         int new_scroll_y = jy - MIDPOSY + offset_y;
13067
13068         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
13069             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13070           scroll_y = new_scroll_y;
13071
13072         // don't scroll over playfield boundaries
13073         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13074
13075         // don't scroll more than one field at a time
13076         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13077
13078         // don't scroll against the player's moving direction
13079         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13080             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13081           scroll_y = old_scroll_y;
13082       }
13083     }
13084
13085     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13086     {
13087       if (!network.enabled && game.centered_player_nr == -1 &&
13088           !AllPlayersInVisibleScreen())
13089       {
13090         scroll_x = old_scroll_x;
13091         scroll_y = old_scroll_y;
13092       }
13093       else
13094       {
13095         ScrollScreen(player, SCROLL_INIT);
13096         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13097       }
13098     }
13099   }
13100
13101   player->StepFrame = 0;
13102
13103   if (moved & MP_MOVING)
13104   {
13105     if (old_jx != jx && old_jy == jy)
13106       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13107     else if (old_jx == jx && old_jy != jy)
13108       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13109
13110     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13111
13112     player->last_move_dir = player->MovDir;
13113     player->is_moving = TRUE;
13114     player->is_snapping = FALSE;
13115     player->is_switching = FALSE;
13116     player->is_dropping = FALSE;
13117     player->is_dropping_pressed = FALSE;
13118     player->drop_pressed_delay = 0;
13119
13120 #if 0
13121     // should better be called here than above, but this breaks some tapes
13122     ScrollPlayer(player, SCROLL_INIT);
13123 #endif
13124   }
13125   else
13126   {
13127     CheckGravityMovementWhenNotMoving(player);
13128
13129     player->is_moving = FALSE;
13130
13131     /* at this point, the player is allowed to move, but cannot move right now
13132        (e.g. because of something blocking the way) -- ensure that the player
13133        is also allowed to move in the next frame (in old versions before 3.1.1,
13134        the player was forced to wait again for eight frames before next try) */
13135
13136     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13137       player->move_delay = 0;   // allow direct movement in the next frame
13138   }
13139
13140   if (player->move_delay == -1)         // not yet initialized by DigField()
13141     player->move_delay = player->move_delay_value;
13142
13143   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13144   {
13145     TestIfPlayerTouchesBadThing(jx, jy);
13146     TestIfPlayerTouchesCustomElement(jx, jy);
13147   }
13148
13149   if (!player->active)
13150     RemovePlayer(player);
13151
13152   return moved;
13153 }
13154
13155 void ScrollPlayer(struct PlayerInfo *player, int mode)
13156 {
13157   int jx = player->jx, jy = player->jy;
13158   int last_jx = player->last_jx, last_jy = player->last_jy;
13159   int move_stepsize = TILEX / player->move_delay_value;
13160
13161   if (!player->active)
13162     return;
13163
13164   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13165     return;
13166
13167   if (mode == SCROLL_INIT)
13168   {
13169     player->actual_frame_counter.count = FrameCounter;
13170     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13171
13172     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13173         Tile[last_jx][last_jy] == EL_EMPTY)
13174     {
13175       int last_field_block_delay = 0;   // start with no blocking at all
13176       int block_delay_adjustment = player->block_delay_adjustment;
13177
13178       // if player blocks last field, add delay for exactly one move
13179       if (player->block_last_field)
13180       {
13181         last_field_block_delay += player->move_delay_value;
13182
13183         // when blocking enabled, prevent moving up despite gravity
13184         if (player->gravity && player->MovDir == MV_UP)
13185           block_delay_adjustment = -1;
13186       }
13187
13188       // add block delay adjustment (also possible when not blocking)
13189       last_field_block_delay += block_delay_adjustment;
13190
13191       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13192       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13193     }
13194
13195     if (player->MovPos != 0)    // player has not yet reached destination
13196       return;
13197   }
13198   else if (!FrameReached(&player->actual_frame_counter))
13199     return;
13200
13201   if (player->MovPos != 0)
13202   {
13203     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13204     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13205
13206     // before DrawPlayer() to draw correct player graphic for this case
13207     if (player->MovPos == 0)
13208       CheckGravityMovement(player);
13209   }
13210
13211   if (player->MovPos == 0)      // player reached destination field
13212   {
13213     if (player->move_delay_reset_counter > 0)
13214     {
13215       player->move_delay_reset_counter--;
13216
13217       if (player->move_delay_reset_counter == 0)
13218       {
13219         // continue with normal speed after quickly moving through gate
13220         HALVE_PLAYER_SPEED(player);
13221
13222         // be able to make the next move without delay
13223         player->move_delay = 0;
13224       }
13225     }
13226
13227     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13228         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13229         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13230         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13231         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13232         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13233         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13234         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13235     {
13236       ExitPlayer(player);
13237
13238       if (game.players_still_needed == 0 &&
13239           (game.friends_still_needed == 0 ||
13240            IS_SP_ELEMENT(Tile[jx][jy])))
13241         LevelSolved();
13242     }
13243
13244     player->last_jx = jx;
13245     player->last_jy = jy;
13246
13247     // this breaks one level: "machine", level 000
13248     {
13249       int move_direction = player->MovDir;
13250       int enter_side = MV_DIR_OPPOSITE(move_direction);
13251       int leave_side = move_direction;
13252       int old_jx = last_jx;
13253       int old_jy = last_jy;
13254       int old_element = Tile[old_jx][old_jy];
13255       int new_element = Tile[jx][jy];
13256
13257       if (IS_CUSTOM_ELEMENT(old_element))
13258         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13259                                    CE_LEFT_BY_PLAYER,
13260                                    player->index_bit, leave_side);
13261
13262       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13263                                           CE_PLAYER_LEAVES_X,
13264                                           player->index_bit, leave_side);
13265
13266       // needed because pushed element has not yet reached its destination,
13267       // so it would trigger a change event at its previous field location
13268       if (!player->is_pushing)
13269       {
13270         if (IS_CUSTOM_ELEMENT(new_element))
13271           CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13272                                      player->index_bit, enter_side);
13273
13274         CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13275                                             CE_PLAYER_ENTERS_X,
13276                                             player->index_bit, enter_side);
13277       }
13278
13279       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13280                                         CE_MOVE_OF_X, move_direction);
13281     }
13282
13283     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13284     {
13285       TestIfPlayerTouchesBadThing(jx, jy);
13286       TestIfPlayerTouchesCustomElement(jx, jy);
13287
13288       // needed because pushed element has not yet reached its destination,
13289       // so it would trigger a change event at its previous field location
13290       if (!player->is_pushing)
13291         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13292
13293       if (level.finish_dig_collect &&
13294           (player->is_digging || player->is_collecting))
13295       {
13296         int last_element = player->last_removed_element;
13297         int move_direction = player->MovDir;
13298         int enter_side = MV_DIR_OPPOSITE(move_direction);
13299         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13300                             CE_PLAYER_COLLECTS_X);
13301
13302         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13303                                             player->index_bit, enter_side);
13304
13305         player->last_removed_element = EL_UNDEFINED;
13306       }
13307
13308       if (!player->active)
13309         RemovePlayer(player);
13310     }
13311
13312     if (level.use_step_counter)
13313       CheckLevelTime_StepCounter();
13314
13315     if (tape.single_step && tape.recording && !tape.pausing &&
13316         !player->programmed_action)
13317       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13318
13319     if (!player->programmed_action)
13320       CheckSaveEngineSnapshot(player);
13321   }
13322 }
13323
13324 void ScrollScreen(struct PlayerInfo *player, int mode)
13325 {
13326   static DelayCounter screen_frame_counter = { 0 };
13327
13328   if (mode == SCROLL_INIT)
13329   {
13330     // set scrolling step size according to actual player's moving speed
13331     ScrollStepSize = TILEX / player->move_delay_value;
13332
13333     screen_frame_counter.count = FrameCounter;
13334     screen_frame_counter.value = 1;
13335
13336     ScreenMovDir = player->MovDir;
13337     ScreenMovPos = player->MovPos;
13338     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13339     return;
13340   }
13341   else if (!FrameReached(&screen_frame_counter))
13342     return;
13343
13344   if (ScreenMovPos)
13345   {
13346     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13347     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13348     redraw_mask |= REDRAW_FIELD;
13349   }
13350   else
13351     ScreenMovDir = MV_NONE;
13352 }
13353
13354 void CheckNextToConditions(int x, int y)
13355 {
13356   int element = Tile[x][y];
13357
13358   if (IS_PLAYER(x, y))
13359     TestIfPlayerNextToCustomElement(x, y);
13360
13361   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13362       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13363     TestIfElementNextToCustomElement(x, y);
13364 }
13365
13366 void TestIfPlayerNextToCustomElement(int x, int y)
13367 {
13368   struct XY *xy = xy_topdown;
13369   static int trigger_sides[4][2] =
13370   {
13371     // center side       border side
13372     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13373     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13374     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13375     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13376   };
13377   int i;
13378
13379   if (!IS_PLAYER(x, y))
13380     return;
13381
13382   struct PlayerInfo *player = PLAYERINFO(x, y);
13383
13384   if (player->is_moving)
13385     return;
13386
13387   for (i = 0; i < NUM_DIRECTIONS; i++)
13388   {
13389     int xx = x + xy[i].x;
13390     int yy = y + xy[i].y;
13391     int border_side = trigger_sides[i][1];
13392     int border_element;
13393
13394     if (!IN_LEV_FIELD(xx, yy))
13395       continue;
13396
13397     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13398       continue;         // center and border element not connected
13399
13400     border_element = Tile[xx][yy];
13401
13402     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13403                                player->index_bit, border_side);
13404     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13405                                         CE_PLAYER_NEXT_TO_X,
13406                                         player->index_bit, border_side);
13407
13408     /* use player element that is initially defined in the level playfield,
13409        not the player element that corresponds to the runtime player number
13410        (example: a level that contains EL_PLAYER_3 as the only player would
13411        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13412
13413     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13414                              CE_NEXT_TO_X, border_side);
13415   }
13416 }
13417
13418 void TestIfPlayerTouchesCustomElement(int x, int y)
13419 {
13420   struct XY *xy = xy_topdown;
13421   static int trigger_sides[4][2] =
13422   {
13423     // center side       border side
13424     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13425     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13426     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13427     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13428   };
13429   static int touch_dir[4] =
13430   {
13431     MV_LEFT | MV_RIGHT,
13432     MV_UP   | MV_DOWN,
13433     MV_UP   | MV_DOWN,
13434     MV_LEFT | MV_RIGHT
13435   };
13436   int center_element = Tile[x][y];      // should always be non-moving!
13437   int i;
13438
13439   for (i = 0; i < NUM_DIRECTIONS; i++)
13440   {
13441     int xx = x + xy[i].x;
13442     int yy = y + xy[i].y;
13443     int center_side = trigger_sides[i][0];
13444     int border_side = trigger_sides[i][1];
13445     int border_element;
13446
13447     if (!IN_LEV_FIELD(xx, yy))
13448       continue;
13449
13450     if (IS_PLAYER(x, y))                // player found at center element
13451     {
13452       struct PlayerInfo *player = PLAYERINFO(x, y);
13453
13454       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13455         border_element = Tile[xx][yy];          // may be moving!
13456       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13457         border_element = Tile[xx][yy];
13458       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13459         border_element = MovingOrBlocked2Element(xx, yy);
13460       else
13461         continue;               // center and border element do not touch
13462
13463       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13464                                  player->index_bit, border_side);
13465       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13466                                           CE_PLAYER_TOUCHES_X,
13467                                           player->index_bit, border_side);
13468
13469       {
13470         /* use player element that is initially defined in the level playfield,
13471            not the player element that corresponds to the runtime player number
13472            (example: a level that contains EL_PLAYER_3 as the only player would
13473            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13474         int player_element = PLAYERINFO(x, y)->initial_element;
13475
13476         // as element "X" is the player here, check opposite (center) side
13477         CheckElementChangeBySide(xx, yy, border_element, player_element,
13478                                  CE_TOUCHING_X, center_side);
13479       }
13480     }
13481     else if (IS_PLAYER(xx, yy))         // player found at border element
13482     {
13483       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13484
13485       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13486       {
13487         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13488           continue;             // center and border element do not touch
13489       }
13490
13491       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13492                                  player->index_bit, center_side);
13493       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13494                                           CE_PLAYER_TOUCHES_X,
13495                                           player->index_bit, center_side);
13496
13497       {
13498         /* use player element that is initially defined in the level playfield,
13499            not the player element that corresponds to the runtime player number
13500            (example: a level that contains EL_PLAYER_3 as the only player would
13501            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13502         int player_element = PLAYERINFO(xx, yy)->initial_element;
13503
13504         // as element "X" is the player here, check opposite (border) side
13505         CheckElementChangeBySide(x, y, center_element, player_element,
13506                                  CE_TOUCHING_X, border_side);
13507       }
13508
13509       break;
13510     }
13511   }
13512 }
13513
13514 void TestIfElementNextToCustomElement(int x, int y)
13515 {
13516   struct XY *xy = xy_topdown;
13517   static int trigger_sides[4][2] =
13518   {
13519     // center side      border side
13520     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13521     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13522     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13523     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13524   };
13525   int center_element = Tile[x][y];      // should always be non-moving!
13526   int i;
13527
13528   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13529     return;
13530
13531   for (i = 0; i < NUM_DIRECTIONS; i++)
13532   {
13533     int xx = x + xy[i].x;
13534     int yy = y + xy[i].y;
13535     int border_side = trigger_sides[i][1];
13536     int border_element;
13537
13538     if (!IN_LEV_FIELD(xx, yy))
13539       continue;
13540
13541     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13542       continue;                 // center and border element not connected
13543
13544     border_element = Tile[xx][yy];
13545
13546     // check for change of center element (but change it only once)
13547     if (CheckElementChangeBySide(x, y, center_element, border_element,
13548                                  CE_NEXT_TO_X, border_side))
13549       break;
13550   }
13551 }
13552
13553 void TestIfElementTouchesCustomElement(int x, int y)
13554 {
13555   struct XY *xy = xy_topdown;
13556   static int trigger_sides[4][2] =
13557   {
13558     // center side      border side
13559     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13560     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13561     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13562     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13563   };
13564   static int touch_dir[4] =
13565   {
13566     MV_LEFT | MV_RIGHT,
13567     MV_UP   | MV_DOWN,
13568     MV_UP   | MV_DOWN,
13569     MV_LEFT | MV_RIGHT
13570   };
13571   boolean change_center_element = FALSE;
13572   int center_element = Tile[x][y];      // should always be non-moving!
13573   int border_element_old[NUM_DIRECTIONS];
13574   int i;
13575
13576   for (i = 0; i < NUM_DIRECTIONS; i++)
13577   {
13578     int xx = x + xy[i].x;
13579     int yy = y + xy[i].y;
13580     int border_element;
13581
13582     border_element_old[i] = -1;
13583
13584     if (!IN_LEV_FIELD(xx, yy))
13585       continue;
13586
13587     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13588       border_element = Tile[xx][yy];    // may be moving!
13589     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13590       border_element = Tile[xx][yy];
13591     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13592       border_element = MovingOrBlocked2Element(xx, yy);
13593     else
13594       continue;                 // center and border element do not touch
13595
13596     border_element_old[i] = border_element;
13597   }
13598
13599   for (i = 0; i < NUM_DIRECTIONS; i++)
13600   {
13601     int xx = x + xy[i].x;
13602     int yy = y + xy[i].y;
13603     int center_side = trigger_sides[i][0];
13604     int border_element = border_element_old[i];
13605
13606     if (border_element == -1)
13607       continue;
13608
13609     // check for change of border element
13610     CheckElementChangeBySide(xx, yy, border_element, center_element,
13611                              CE_TOUCHING_X, center_side);
13612
13613     // (center element cannot be player, so we don't have to check this here)
13614   }
13615
13616   for (i = 0; i < NUM_DIRECTIONS; i++)
13617   {
13618     int xx = x + xy[i].x;
13619     int yy = y + xy[i].y;
13620     int border_side = trigger_sides[i][1];
13621     int border_element = border_element_old[i];
13622
13623     if (border_element == -1)
13624       continue;
13625
13626     // check for change of center element (but change it only once)
13627     if (!change_center_element)
13628       change_center_element =
13629         CheckElementChangeBySide(x, y, center_element, border_element,
13630                                  CE_TOUCHING_X, border_side);
13631
13632     if (IS_PLAYER(xx, yy))
13633     {
13634       /* use player element that is initially defined in the level playfield,
13635          not the player element that corresponds to the runtime player number
13636          (example: a level that contains EL_PLAYER_3 as the only player would
13637          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13638       int player_element = PLAYERINFO(xx, yy)->initial_element;
13639
13640       // as element "X" is the player here, check opposite (border) side
13641       CheckElementChangeBySide(x, y, center_element, player_element,
13642                                CE_TOUCHING_X, border_side);
13643     }
13644   }
13645 }
13646
13647 void TestIfElementHitsCustomElement(int x, int y, int direction)
13648 {
13649   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13650   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13651   int hitx = x + dx, hity = y + dy;
13652   int hitting_element = Tile[x][y];
13653   int touched_element;
13654
13655   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13656     return;
13657
13658   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13659                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13660
13661   if (IN_LEV_FIELD(hitx, hity))
13662   {
13663     int opposite_direction = MV_DIR_OPPOSITE(direction);
13664     int hitting_side = direction;
13665     int touched_side = opposite_direction;
13666     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13667                           MovDir[hitx][hity] != direction ||
13668                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13669
13670     object_hit = TRUE;
13671
13672     if (object_hit)
13673     {
13674       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13675                                CE_HITTING_X, touched_side);
13676
13677       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13678                                CE_HIT_BY_X, hitting_side);
13679
13680       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13681                                CE_HIT_BY_SOMETHING, opposite_direction);
13682
13683       if (IS_PLAYER(hitx, hity))
13684       {
13685         /* use player element that is initially defined in the level playfield,
13686            not the player element that corresponds to the runtime player number
13687            (example: a level that contains EL_PLAYER_3 as the only player would
13688            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13689         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13690
13691         CheckElementChangeBySide(x, y, hitting_element, player_element,
13692                                  CE_HITTING_X, touched_side);
13693       }
13694     }
13695   }
13696
13697   // "hitting something" is also true when hitting the playfield border
13698   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13699                            CE_HITTING_SOMETHING, direction);
13700 }
13701
13702 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13703 {
13704   int i, kill_x = -1, kill_y = -1;
13705
13706   int bad_element = -1;
13707   struct XY *test_xy = xy_topdown;
13708   static int test_dir[4] =
13709   {
13710     MV_UP,
13711     MV_LEFT,
13712     MV_RIGHT,
13713     MV_DOWN
13714   };
13715
13716   for (i = 0; i < NUM_DIRECTIONS; i++)
13717   {
13718     int test_x, test_y, test_move_dir, test_element;
13719
13720     test_x = good_x + test_xy[i].x;
13721     test_y = good_y + test_xy[i].y;
13722
13723     if (!IN_LEV_FIELD(test_x, test_y))
13724       continue;
13725
13726     test_move_dir =
13727       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13728
13729     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13730
13731     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13732        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13733     */
13734     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13735         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13736     {
13737       kill_x = test_x;
13738       kill_y = test_y;
13739       bad_element = test_element;
13740
13741       break;
13742     }
13743   }
13744
13745   if (kill_x != -1 || kill_y != -1)
13746   {
13747     if (IS_PLAYER(good_x, good_y))
13748     {
13749       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13750
13751       if (player->shield_deadly_time_left > 0 &&
13752           !IS_INDESTRUCTIBLE(bad_element))
13753         Bang(kill_x, kill_y);
13754       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13755         KillPlayer(player);
13756     }
13757     else
13758       Bang(good_x, good_y);
13759   }
13760 }
13761
13762 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13763 {
13764   int i, kill_x = -1, kill_y = -1;
13765   int bad_element = Tile[bad_x][bad_y];
13766   struct XY *test_xy = xy_topdown;
13767   static int touch_dir[4] =
13768   {
13769     MV_LEFT | MV_RIGHT,
13770     MV_UP   | MV_DOWN,
13771     MV_UP   | MV_DOWN,
13772     MV_LEFT | MV_RIGHT
13773   };
13774   static int test_dir[4] =
13775   {
13776     MV_UP,
13777     MV_LEFT,
13778     MV_RIGHT,
13779     MV_DOWN
13780   };
13781
13782   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13783     return;
13784
13785   for (i = 0; i < NUM_DIRECTIONS; i++)
13786   {
13787     int test_x, test_y, test_move_dir, test_element;
13788
13789     test_x = bad_x + test_xy[i].x;
13790     test_y = bad_y + test_xy[i].y;
13791
13792     if (!IN_LEV_FIELD(test_x, test_y))
13793       continue;
13794
13795     test_move_dir =
13796       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13797
13798     test_element = Tile[test_x][test_y];
13799
13800     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13801        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13802     */
13803     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13804         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13805     {
13806       // good thing is player or penguin that does not move away
13807       if (IS_PLAYER(test_x, test_y))
13808       {
13809         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13810
13811         if (bad_element == EL_ROBOT && player->is_moving)
13812           continue;     // robot does not kill player if he is moving
13813
13814         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13815         {
13816           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13817             continue;           // center and border element do not touch
13818         }
13819
13820         kill_x = test_x;
13821         kill_y = test_y;
13822
13823         break;
13824       }
13825       else if (test_element == EL_PENGUIN)
13826       {
13827         kill_x = test_x;
13828         kill_y = test_y;
13829
13830         break;
13831       }
13832     }
13833   }
13834
13835   if (kill_x != -1 || kill_y != -1)
13836   {
13837     if (IS_PLAYER(kill_x, kill_y))
13838     {
13839       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13840
13841       if (player->shield_deadly_time_left > 0 &&
13842           !IS_INDESTRUCTIBLE(bad_element))
13843         Bang(bad_x, bad_y);
13844       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13845         KillPlayer(player);
13846     }
13847     else
13848       Bang(kill_x, kill_y);
13849   }
13850 }
13851
13852 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13853 {
13854   int bad_element = Tile[bad_x][bad_y];
13855   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13856   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13857   int test_x = bad_x + dx, test_y = bad_y + dy;
13858   int test_move_dir, test_element;
13859   int kill_x = -1, kill_y = -1;
13860
13861   if (!IN_LEV_FIELD(test_x, test_y))
13862     return;
13863
13864   test_move_dir =
13865     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13866
13867   test_element = Tile[test_x][test_y];
13868
13869   if (test_move_dir != bad_move_dir)
13870   {
13871     // good thing can be player or penguin that does not move away
13872     if (IS_PLAYER(test_x, test_y))
13873     {
13874       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13875
13876       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13877          player as being hit when he is moving towards the bad thing, because
13878          the "get hit by" condition would be lost after the player stops) */
13879       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13880         return;         // player moves away from bad thing
13881
13882       kill_x = test_x;
13883       kill_y = test_y;
13884     }
13885     else if (test_element == EL_PENGUIN)
13886     {
13887       kill_x = test_x;
13888       kill_y = test_y;
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 TestIfPlayerTouchesBadThing(int x, int y)
13910 {
13911   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13912 }
13913
13914 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13915 {
13916   TestIfGoodThingHitsBadThing(x, y, move_dir);
13917 }
13918
13919 void TestIfBadThingTouchesPlayer(int x, int y)
13920 {
13921   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13922 }
13923
13924 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13925 {
13926   TestIfBadThingHitsGoodThing(x, y, move_dir);
13927 }
13928
13929 void TestIfFriendTouchesBadThing(int x, int y)
13930 {
13931   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13932 }
13933
13934 void TestIfBadThingTouchesFriend(int x, int y)
13935 {
13936   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13937 }
13938
13939 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13940 {
13941   int i, kill_x = bad_x, kill_y = bad_y;
13942   struct XY *xy = xy_topdown;
13943
13944   for (i = 0; i < NUM_DIRECTIONS; i++)
13945   {
13946     int x, y, element;
13947
13948     x = bad_x + xy[i].x;
13949     y = bad_y + xy[i].y;
13950     if (!IN_LEV_FIELD(x, y))
13951       continue;
13952
13953     element = Tile[x][y];
13954     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13955         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13956     {
13957       kill_x = x;
13958       kill_y = y;
13959       break;
13960     }
13961   }
13962
13963   if (kill_x != bad_x || kill_y != bad_y)
13964     Bang(bad_x, bad_y);
13965 }
13966
13967 void KillPlayer(struct PlayerInfo *player)
13968 {
13969   int jx = player->jx, jy = player->jy;
13970
13971   if (!player->active)
13972     return;
13973
13974 #if 0
13975   Debug("game:playing:KillPlayer",
13976         "0: killed == %d, active == %d, reanimated == %d",
13977         player->killed, player->active, player->reanimated);
13978 #endif
13979
13980   /* the following code was introduced to prevent an infinite loop when calling
13981      -> Bang()
13982      -> CheckTriggeredElementChangeExt()
13983      -> ExecuteCustomElementAction()
13984      -> KillPlayer()
13985      -> (infinitely repeating the above sequence of function calls)
13986      which occurs when killing the player while having a CE with the setting
13987      "kill player X when explosion of <player X>"; the solution using a new
13988      field "player->killed" was chosen for backwards compatibility, although
13989      clever use of the fields "player->active" etc. would probably also work */
13990 #if 1
13991   if (player->killed)
13992     return;
13993 #endif
13994
13995   player->killed = TRUE;
13996
13997   // remove accessible field at the player's position
13998   RemoveField(jx, jy);
13999
14000   // deactivate shield (else Bang()/Explode() would not work right)
14001   player->shield_normal_time_left = 0;
14002   player->shield_deadly_time_left = 0;
14003
14004 #if 0
14005   Debug("game:playing:KillPlayer",
14006         "1: killed == %d, active == %d, reanimated == %d",
14007         player->killed, player->active, player->reanimated);
14008 #endif
14009
14010   Bang(jx, jy);
14011
14012 #if 0
14013   Debug("game:playing:KillPlayer",
14014         "2: killed == %d, active == %d, reanimated == %d",
14015         player->killed, player->active, player->reanimated);
14016 #endif
14017
14018   if (player->reanimated)       // killed player may have been reanimated
14019     player->killed = player->reanimated = FALSE;
14020   else
14021     BuryPlayer(player);
14022 }
14023
14024 static void KillPlayerUnlessEnemyProtected(int x, int y)
14025 {
14026   if (!PLAYER_ENEMY_PROTECTED(x, y))
14027     KillPlayer(PLAYERINFO(x, y));
14028 }
14029
14030 static void KillPlayerUnlessExplosionProtected(int x, int y)
14031 {
14032   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14033     KillPlayer(PLAYERINFO(x, y));
14034 }
14035
14036 void BuryPlayer(struct PlayerInfo *player)
14037 {
14038   int jx = player->jx, jy = player->jy;
14039
14040   if (!player->active)
14041     return;
14042
14043   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14044
14045   RemovePlayer(player);
14046
14047   player->buried = TRUE;
14048
14049   if (game.all_players_gone)
14050     game.GameOver = TRUE;
14051 }
14052
14053 void RemovePlayer(struct PlayerInfo *player)
14054 {
14055   int jx = player->jx, jy = player->jy;
14056   int i, found = FALSE;
14057
14058   player->present = FALSE;
14059   player->active = FALSE;
14060
14061   // required for some CE actions (even if the player is not active anymore)
14062   player->MovPos = 0;
14063
14064   if (!ExplodeField[jx][jy])
14065     StorePlayer[jx][jy] = 0;
14066
14067   if (player->is_moving)
14068     TEST_DrawLevelField(player->last_jx, player->last_jy);
14069
14070   for (i = 0; i < MAX_PLAYERS; i++)
14071     if (stored_player[i].active)
14072       found = TRUE;
14073
14074   if (!found)
14075   {
14076     game.all_players_gone = TRUE;
14077     game.GameOver = TRUE;
14078   }
14079
14080   game.exit_x = game.robot_wheel_x = jx;
14081   game.exit_y = game.robot_wheel_y = jy;
14082 }
14083
14084 void ExitPlayer(struct PlayerInfo *player)
14085 {
14086   DrawPlayer(player);   // needed here only to cleanup last field
14087   RemovePlayer(player);
14088
14089   if (game.players_still_needed > 0)
14090     game.players_still_needed--;
14091 }
14092
14093 static void SetFieldForSnapping(int x, int y, int element, int direction,
14094                                 int player_index_bit)
14095 {
14096   struct ElementInfo *ei = &element_info[element];
14097   int direction_bit = MV_DIR_TO_BIT(direction);
14098   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14099   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14100                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14101
14102   Tile[x][y] = EL_ELEMENT_SNAPPING;
14103   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14104   MovDir[x][y] = direction;
14105   Store[x][y] = element;
14106   Store2[x][y] = player_index_bit;
14107
14108   ResetGfxAnimation(x, y);
14109
14110   GfxElement[x][y] = element;
14111   GfxAction[x][y] = action;
14112   GfxDir[x][y] = direction;
14113   GfxFrame[x][y] = -1;
14114 }
14115
14116 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14117                                    int player_index_bit)
14118 {
14119   TestIfElementTouchesCustomElement(x, y);      // for empty space
14120
14121   if (level.finish_dig_collect)
14122   {
14123     int dig_side = MV_DIR_OPPOSITE(direction);
14124     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14125                         CE_PLAYER_COLLECTS_X);
14126
14127     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14128                                         player_index_bit, dig_side);
14129     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14130                                         player_index_bit, dig_side);
14131   }
14132 }
14133
14134 /*
14135   =============================================================================
14136   checkDiagonalPushing()
14137   -----------------------------------------------------------------------------
14138   check if diagonal input device direction results in pushing of object
14139   (by checking if the alternative direction is walkable, diggable, ...)
14140   =============================================================================
14141 */
14142
14143 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14144                                     int x, int y, int real_dx, int real_dy)
14145 {
14146   int jx, jy, dx, dy, xx, yy;
14147
14148   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14149     return TRUE;
14150
14151   // diagonal direction: check alternative direction
14152   jx = player->jx;
14153   jy = player->jy;
14154   dx = x - jx;
14155   dy = y - jy;
14156   xx = jx + (dx == 0 ? real_dx : 0);
14157   yy = jy + (dy == 0 ? real_dy : 0);
14158
14159   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14160 }
14161
14162 /*
14163   =============================================================================
14164   DigField()
14165   -----------------------------------------------------------------------------
14166   x, y:                 field next to player (non-diagonal) to try to dig to
14167   real_dx, real_dy:     direction as read from input device (can be diagonal)
14168   =============================================================================
14169 */
14170
14171 static int DigField(struct PlayerInfo *player,
14172                     int oldx, int oldy, int x, int y,
14173                     int real_dx, int real_dy, int mode)
14174 {
14175   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14176   boolean player_was_pushing = player->is_pushing;
14177   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14178   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14179   int jx = oldx, jy = oldy;
14180   int dx = x - jx, dy = y - jy;
14181   int nextx = x + dx, nexty = y + dy;
14182   int move_direction = (dx == -1 ? MV_LEFT  :
14183                         dx == +1 ? MV_RIGHT :
14184                         dy == -1 ? MV_UP    :
14185                         dy == +1 ? MV_DOWN  : MV_NONE);
14186   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14187   int dig_side = MV_DIR_OPPOSITE(move_direction);
14188   int old_element = Tile[jx][jy];
14189   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14190   int collect_count;
14191
14192   if (is_player)                // function can also be called by EL_PENGUIN
14193   {
14194     if (player->MovPos == 0)
14195     {
14196       player->is_digging = FALSE;
14197       player->is_collecting = FALSE;
14198     }
14199
14200     if (player->MovPos == 0)    // last pushing move finished
14201       player->is_pushing = FALSE;
14202
14203     if (mode == DF_NO_PUSH)     // player just stopped pushing
14204     {
14205       player->is_switching = FALSE;
14206       player->push_delay = -1;
14207
14208       return MP_NO_ACTION;
14209     }
14210   }
14211   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14212     old_element = Back[jx][jy];
14213
14214   // in case of element dropped at player position, check background
14215   else if (Back[jx][jy] != EL_EMPTY &&
14216            game.engine_version >= VERSION_IDENT(2,2,0,0))
14217     old_element = Back[jx][jy];
14218
14219   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14220     return MP_NO_ACTION;        // field has no opening in this direction
14221
14222   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element, opposite_direction))
14223     return MP_NO_ACTION;        // field has no opening in this direction
14224
14225   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14226   {
14227     SplashAcid(x, y);
14228
14229     Tile[jx][jy] = player->artwork_element;
14230     InitMovingField(jx, jy, MV_DOWN);
14231     Store[jx][jy] = EL_ACID;
14232     ContinueMoving(jx, jy);
14233     BuryPlayer(player);
14234
14235     return MP_DONT_RUN_INTO;
14236   }
14237
14238   if (player_can_move && DONT_RUN_INTO(element))
14239   {
14240     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14241
14242     return MP_DONT_RUN_INTO;
14243   }
14244
14245   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14246     return MP_NO_ACTION;
14247
14248   collect_count = element_info[element].collect_count_initial;
14249
14250   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14251     return MP_NO_ACTION;
14252
14253   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14254     player_can_move = player_can_move_or_snap;
14255
14256   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14257       game.engine_version >= VERSION_IDENT(2,2,0,0))
14258   {
14259     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14260                                player->index_bit, dig_side);
14261     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14262                                         player->index_bit, dig_side);
14263
14264     if (element == EL_DC_LANDMINE)
14265       Bang(x, y);
14266
14267     if (Tile[x][y] != element)          // field changed by snapping
14268       return MP_ACTION;
14269
14270     return MP_NO_ACTION;
14271   }
14272
14273   if (player->gravity && is_player && !player->is_auto_moving &&
14274       canFallDown(player) && move_direction != MV_DOWN &&
14275       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14276     return MP_NO_ACTION;        // player cannot walk here due to gravity
14277
14278   if (player_can_move &&
14279       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14280   {
14281     int sound_element = SND_ELEMENT(element);
14282     int sound_action = ACTION_WALKING;
14283
14284     if (IS_RND_GATE(element))
14285     {
14286       if (!player->key[RND_GATE_NR(element)])
14287         return MP_NO_ACTION;
14288     }
14289     else if (IS_RND_GATE_GRAY(element))
14290     {
14291       if (!player->key[RND_GATE_GRAY_NR(element)])
14292         return MP_NO_ACTION;
14293     }
14294     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14295     {
14296       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14297         return MP_NO_ACTION;
14298     }
14299     else if (element == EL_EXIT_OPEN ||
14300              element == EL_EM_EXIT_OPEN ||
14301              element == EL_EM_EXIT_OPENING ||
14302              element == EL_STEEL_EXIT_OPEN ||
14303              element == EL_EM_STEEL_EXIT_OPEN ||
14304              element == EL_EM_STEEL_EXIT_OPENING ||
14305              element == EL_SP_EXIT_OPEN ||
14306              element == EL_SP_EXIT_OPENING)
14307     {
14308       sound_action = ACTION_PASSING;    // player is passing exit
14309     }
14310     else if (element == EL_EMPTY)
14311     {
14312       sound_action = ACTION_MOVING;             // nothing to walk on
14313     }
14314
14315     // play sound from background or player, whatever is available
14316     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14317       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14318     else
14319       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14320   }
14321   else if (player_can_move &&
14322            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14323   {
14324     if (!ACCESS_FROM(element, opposite_direction))
14325       return MP_NO_ACTION;      // field not accessible from this direction
14326
14327     if (CAN_MOVE(element))      // only fixed elements can be passed!
14328       return MP_NO_ACTION;
14329
14330     if (IS_EM_GATE(element))
14331     {
14332       if (!player->key[EM_GATE_NR(element)])
14333         return MP_NO_ACTION;
14334     }
14335     else if (IS_EM_GATE_GRAY(element))
14336     {
14337       if (!player->key[EM_GATE_GRAY_NR(element)])
14338         return MP_NO_ACTION;
14339     }
14340     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14341     {
14342       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14343         return MP_NO_ACTION;
14344     }
14345     else if (IS_EMC_GATE(element))
14346     {
14347       if (!player->key[EMC_GATE_NR(element)])
14348         return MP_NO_ACTION;
14349     }
14350     else if (IS_EMC_GATE_GRAY(element))
14351     {
14352       if (!player->key[EMC_GATE_GRAY_NR(element)])
14353         return MP_NO_ACTION;
14354     }
14355     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14356     {
14357       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14358         return MP_NO_ACTION;
14359     }
14360     else if (element == EL_DC_GATE_WHITE ||
14361              element == EL_DC_GATE_WHITE_GRAY ||
14362              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14363     {
14364       if (player->num_white_keys == 0)
14365         return MP_NO_ACTION;
14366
14367       player->num_white_keys--;
14368     }
14369     else if (IS_SP_PORT(element))
14370     {
14371       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14372           element == EL_SP_GRAVITY_PORT_RIGHT ||
14373           element == EL_SP_GRAVITY_PORT_UP ||
14374           element == EL_SP_GRAVITY_PORT_DOWN)
14375         player->gravity = !player->gravity;
14376       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14377                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14378                element == EL_SP_GRAVITY_ON_PORT_UP ||
14379                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14380         player->gravity = TRUE;
14381       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14382                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14383                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14384                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14385         player->gravity = FALSE;
14386     }
14387
14388     // automatically move to the next field with double speed
14389     player->programmed_action = move_direction;
14390
14391     if (player->move_delay_reset_counter == 0)
14392     {
14393       player->move_delay_reset_counter = 2;     // two double speed steps
14394
14395       DOUBLE_PLAYER_SPEED(player);
14396     }
14397
14398     PlayLevelSoundAction(x, y, ACTION_PASSING);
14399   }
14400   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14401   {
14402     RemoveField(x, y);
14403
14404     if (mode != DF_SNAP)
14405     {
14406       GfxElement[x][y] = GFX_ELEMENT(element);
14407       player->is_digging = TRUE;
14408     }
14409
14410     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14411
14412     // use old behaviour for old levels (digging)
14413     if (!level.finish_dig_collect)
14414     {
14415       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14416                                           player->index_bit, dig_side);
14417
14418       // if digging triggered player relocation, finish digging tile
14419       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14420         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14421     }
14422
14423     if (mode == DF_SNAP)
14424     {
14425       if (level.block_snap_field)
14426         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14427       else
14428         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14429
14430       // use old behaviour for old levels (snapping)
14431       if (!level.finish_dig_collect)
14432         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14433                                             player->index_bit, dig_side);
14434     }
14435   }
14436   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14437   {
14438     RemoveField(x, y);
14439
14440     if (is_player && mode != DF_SNAP)
14441     {
14442       GfxElement[x][y] = element;
14443       player->is_collecting = TRUE;
14444     }
14445
14446     if (element == EL_SPEED_PILL)
14447     {
14448       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14449     }
14450     else if (element == EL_EXTRA_TIME && level.time > 0)
14451     {
14452       TimeLeft += level.extra_time;
14453
14454       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14455
14456       DisplayGameControlValues();
14457     }
14458     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14459     {
14460       int shield_time = (element == EL_SHIELD_DEADLY ?
14461                          level.shield_deadly_time :
14462                          level.shield_normal_time);
14463
14464       player->shield_normal_time_left += shield_time;
14465       if (element == EL_SHIELD_DEADLY)
14466         player->shield_deadly_time_left += shield_time;
14467     }
14468     else if (element == EL_DYNAMITE ||
14469              element == EL_EM_DYNAMITE ||
14470              element == EL_SP_DISK_RED)
14471     {
14472       if (player->inventory_size < MAX_INVENTORY_SIZE)
14473         player->inventory_element[player->inventory_size++] = element;
14474
14475       DrawGameDoorValues();
14476     }
14477     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14478     {
14479       player->dynabomb_count++;
14480       player->dynabombs_left++;
14481     }
14482     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14483     {
14484       player->dynabomb_size++;
14485     }
14486     else if (element == EL_DYNABOMB_INCREASE_POWER)
14487     {
14488       player->dynabomb_xl = TRUE;
14489     }
14490     else if (IS_KEY(element))
14491     {
14492       player->key[KEY_NR(element)] = TRUE;
14493
14494       DrawGameDoorValues();
14495     }
14496     else if (element == EL_DC_KEY_WHITE)
14497     {
14498       player->num_white_keys++;
14499
14500       // display white keys?
14501       // DrawGameDoorValues();
14502     }
14503     else if (IS_ENVELOPE(element))
14504     {
14505       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14506
14507       if (!wait_for_snapping)
14508         player->show_envelope = element;
14509     }
14510     else if (element == EL_EMC_LENSES)
14511     {
14512       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14513
14514       RedrawAllInvisibleElementsForLenses();
14515     }
14516     else if (element == EL_EMC_MAGNIFIER)
14517     {
14518       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14519
14520       RedrawAllInvisibleElementsForMagnifier();
14521     }
14522     else if (IS_DROPPABLE(element) ||
14523              IS_THROWABLE(element))     // can be collected and dropped
14524     {
14525       int i;
14526
14527       if (collect_count == 0)
14528         player->inventory_infinite_element = element;
14529       else
14530         for (i = 0; i < collect_count; i++)
14531           if (player->inventory_size < MAX_INVENTORY_SIZE)
14532             player->inventory_element[player->inventory_size++] = element;
14533
14534       DrawGameDoorValues();
14535     }
14536     else if (collect_count > 0)
14537     {
14538       game.gems_still_needed -= collect_count;
14539       if (game.gems_still_needed < 0)
14540         game.gems_still_needed = 0;
14541
14542       game.snapshot.collected_item = TRUE;
14543
14544       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14545
14546       DisplayGameControlValues();
14547     }
14548
14549     RaiseScoreElement(element);
14550     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14551
14552     // use old behaviour for old levels (collecting)
14553     if (!level.finish_dig_collect && is_player)
14554     {
14555       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14556                                           player->index_bit, dig_side);
14557
14558       // if collecting triggered player relocation, finish collecting tile
14559       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14560         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14561     }
14562
14563     if (mode == DF_SNAP)
14564     {
14565       if (level.block_snap_field)
14566         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14567       else
14568         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14569
14570       // use old behaviour for old levels (snapping)
14571       if (!level.finish_dig_collect)
14572         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14573                                             player->index_bit, dig_side);
14574     }
14575   }
14576   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14577   {
14578     if (mode == DF_SNAP && element != EL_BD_ROCK)
14579       return MP_NO_ACTION;
14580
14581     if (CAN_FALL(element) && dy)
14582       return MP_NO_ACTION;
14583
14584     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14585         !(element == EL_SPRING && level.use_spring_bug))
14586       return MP_NO_ACTION;
14587
14588     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14589         ((move_direction & MV_VERTICAL &&
14590           ((element_info[element].move_pattern & MV_LEFT &&
14591             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14592            (element_info[element].move_pattern & MV_RIGHT &&
14593             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14594          (move_direction & MV_HORIZONTAL &&
14595           ((element_info[element].move_pattern & MV_UP &&
14596             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14597            (element_info[element].move_pattern & MV_DOWN &&
14598             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14599       return MP_NO_ACTION;
14600
14601     // do not push elements already moving away faster than player
14602     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14603         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14604       return MP_NO_ACTION;
14605
14606     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14607     {
14608       if (player->push_delay_value == -1 || !player_was_pushing)
14609         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14610     }
14611     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14612     {
14613       if (player->push_delay_value == -1)
14614         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14615     }
14616     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14617     {
14618       if (!player->is_pushing)
14619         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14620     }
14621
14622     player->is_pushing = TRUE;
14623     player->is_active = TRUE;
14624
14625     if (!(IN_LEV_FIELD(nextx, nexty) &&
14626           (IS_FREE(nextx, nexty) ||
14627            (IS_SB_ELEMENT(element) &&
14628             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14629            (IS_CUSTOM_ELEMENT(element) &&
14630             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14631       return MP_NO_ACTION;
14632
14633     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14634       return MP_NO_ACTION;
14635
14636     if (player->push_delay == -1)       // new pushing; restart delay
14637       player->push_delay = 0;
14638
14639     if (player->push_delay < player->push_delay_value &&
14640         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14641         element != EL_SPRING && element != EL_BALLOON)
14642     {
14643       // make sure that there is no move delay before next try to push
14644       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14645         player->move_delay = 0;
14646
14647       return MP_NO_ACTION;
14648     }
14649
14650     if (IS_CUSTOM_ELEMENT(element) &&
14651         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14652     {
14653       if (!DigFieldByCE(nextx, nexty, element))
14654         return MP_NO_ACTION;
14655     }
14656
14657     if (IS_SB_ELEMENT(element))
14658     {
14659       boolean sokoban_task_solved = FALSE;
14660
14661       if (element == EL_SOKOBAN_FIELD_FULL)
14662       {
14663         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14664
14665         IncrementSokobanFieldsNeeded();
14666         IncrementSokobanObjectsNeeded();
14667       }
14668
14669       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14670       {
14671         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14672
14673         DecrementSokobanFieldsNeeded();
14674         DecrementSokobanObjectsNeeded();
14675
14676         // sokoban object was pushed from empty field to sokoban field
14677         if (Back[x][y] == EL_EMPTY)
14678           sokoban_task_solved = TRUE;
14679       }
14680
14681       Tile[x][y] = EL_SOKOBAN_OBJECT;
14682
14683       if (Back[x][y] == Back[nextx][nexty])
14684         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14685       else if (Back[x][y] != 0)
14686         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14687                                     ACTION_EMPTYING);
14688       else
14689         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14690                                     ACTION_FILLING);
14691
14692       if (sokoban_task_solved &&
14693           game.sokoban_fields_still_needed == 0 &&
14694           game.sokoban_objects_still_needed == 0 &&
14695           level.auto_exit_sokoban)
14696       {
14697         game.players_still_needed = 0;
14698
14699         LevelSolved();
14700
14701         PlaySound(SND_GAME_SOKOBAN_SOLVING);
14702       }
14703     }
14704     else
14705       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14706
14707     InitMovingField(x, y, move_direction);
14708     GfxAction[x][y] = ACTION_PUSHING;
14709
14710     if (mode == DF_SNAP)
14711       ContinueMoving(x, y);
14712     else
14713       MovPos[x][y] = (dx != 0 ? dx : dy);
14714
14715     Pushed[x][y] = TRUE;
14716     Pushed[nextx][nexty] = TRUE;
14717
14718     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14719       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14720     else
14721       player->push_delay_value = -1;    // get new value later
14722
14723     // check for element change _after_ element has been pushed
14724     if (game.use_change_when_pushing_bug)
14725     {
14726       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14727                                  player->index_bit, dig_side);
14728       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14729                                           player->index_bit, dig_side);
14730     }
14731   }
14732   else if (IS_SWITCHABLE(element))
14733   {
14734     if (PLAYER_SWITCHING(player, x, y))
14735     {
14736       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14737                                           player->index_bit, dig_side);
14738
14739       return MP_ACTION;
14740     }
14741
14742     player->is_switching = TRUE;
14743     player->switch_x = x;
14744     player->switch_y = y;
14745
14746     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14747
14748     if (element == EL_ROBOT_WHEEL)
14749     {
14750       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14751
14752       game.robot_wheel_x = x;
14753       game.robot_wheel_y = y;
14754       game.robot_wheel_active = TRUE;
14755
14756       TEST_DrawLevelField(x, y);
14757     }
14758     else if (element == EL_SP_TERMINAL)
14759     {
14760       int xx, yy;
14761
14762       SCAN_PLAYFIELD(xx, yy)
14763       {
14764         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14765         {
14766           Bang(xx, yy);
14767         }
14768         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14769         {
14770           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14771
14772           ResetGfxAnimation(xx, yy);
14773           TEST_DrawLevelField(xx, yy);
14774         }
14775       }
14776     }
14777     else if (IS_BELT_SWITCH(element))
14778     {
14779       ToggleBeltSwitch(x, y);
14780     }
14781     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14782              element == EL_SWITCHGATE_SWITCH_DOWN ||
14783              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14784              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14785     {
14786       ToggleSwitchgateSwitch();
14787     }
14788     else if (element == EL_LIGHT_SWITCH ||
14789              element == EL_LIGHT_SWITCH_ACTIVE)
14790     {
14791       ToggleLightSwitch(x, y);
14792     }
14793     else if (element == EL_TIMEGATE_SWITCH ||
14794              element == EL_DC_TIMEGATE_SWITCH)
14795     {
14796       ActivateTimegateSwitch(x, y);
14797     }
14798     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14799              element == EL_BALLOON_SWITCH_RIGHT ||
14800              element == EL_BALLOON_SWITCH_UP    ||
14801              element == EL_BALLOON_SWITCH_DOWN  ||
14802              element == EL_BALLOON_SWITCH_NONE  ||
14803              element == EL_BALLOON_SWITCH_ANY)
14804     {
14805       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14806                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14807                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14808                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14809                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14810                              move_direction);
14811     }
14812     else if (element == EL_LAMP)
14813     {
14814       Tile[x][y] = EL_LAMP_ACTIVE;
14815       game.lights_still_needed--;
14816
14817       ResetGfxAnimation(x, y);
14818       TEST_DrawLevelField(x, y);
14819     }
14820     else if (element == EL_TIME_ORB_FULL)
14821     {
14822       Tile[x][y] = EL_TIME_ORB_EMPTY;
14823
14824       if (level.time > 0 || level.use_time_orb_bug)
14825       {
14826         TimeLeft += level.time_orb_time;
14827         game.no_level_time_limit = FALSE;
14828
14829         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14830
14831         DisplayGameControlValues();
14832       }
14833
14834       ResetGfxAnimation(x, y);
14835       TEST_DrawLevelField(x, y);
14836     }
14837     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14838              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14839     {
14840       int xx, yy;
14841
14842       game.ball_active = !game.ball_active;
14843
14844       SCAN_PLAYFIELD(xx, yy)
14845       {
14846         int e = Tile[xx][yy];
14847
14848         if (game.ball_active)
14849         {
14850           if (e == EL_EMC_MAGIC_BALL)
14851             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14852           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14853             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14854         }
14855         else
14856         {
14857           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14858             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14859           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14860             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14861         }
14862       }
14863     }
14864
14865     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14866                                         player->index_bit, dig_side);
14867
14868     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14869                                         player->index_bit, dig_side);
14870
14871     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14872                                         player->index_bit, dig_side);
14873
14874     return MP_ACTION;
14875   }
14876   else
14877   {
14878     if (!PLAYER_SWITCHING(player, x, y))
14879     {
14880       player->is_switching = TRUE;
14881       player->switch_x = x;
14882       player->switch_y = y;
14883
14884       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14885                                  player->index_bit, dig_side);
14886       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14887                                           player->index_bit, dig_side);
14888
14889       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14890                                  player->index_bit, dig_side);
14891       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14892                                           player->index_bit, dig_side);
14893     }
14894
14895     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14896                                player->index_bit, dig_side);
14897     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14898                                         player->index_bit, dig_side);
14899
14900     return MP_NO_ACTION;
14901   }
14902
14903   player->push_delay = -1;
14904
14905   if (is_player)                // function can also be called by EL_PENGUIN
14906   {
14907     if (Tile[x][y] != element)          // really digged/collected something
14908     {
14909       player->is_collecting = !player->is_digging;
14910       player->is_active = TRUE;
14911
14912       player->last_removed_element = element;
14913     }
14914   }
14915
14916   return MP_MOVING;
14917 }
14918
14919 static boolean DigFieldByCE(int x, int y, int digging_element)
14920 {
14921   int element = Tile[x][y];
14922
14923   if (!IS_FREE(x, y))
14924   {
14925     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14926                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14927                   ACTION_BREAKING);
14928
14929     // no element can dig solid indestructible elements
14930     if (IS_INDESTRUCTIBLE(element) &&
14931         !IS_DIGGABLE(element) &&
14932         !IS_COLLECTIBLE(element))
14933       return FALSE;
14934
14935     if (AmoebaNr[x][y] &&
14936         (element == EL_AMOEBA_FULL ||
14937          element == EL_BD_AMOEBA ||
14938          element == EL_AMOEBA_GROWING))
14939     {
14940       AmoebaCnt[AmoebaNr[x][y]]--;
14941       AmoebaCnt2[AmoebaNr[x][y]]--;
14942     }
14943
14944     if (IS_MOVING(x, y))
14945       RemoveMovingField(x, y);
14946     else
14947     {
14948       RemoveField(x, y);
14949       TEST_DrawLevelField(x, y);
14950     }
14951
14952     // if digged element was about to explode, prevent the explosion
14953     ExplodeField[x][y] = EX_TYPE_NONE;
14954
14955     PlayLevelSoundAction(x, y, action);
14956   }
14957
14958   Store[x][y] = EL_EMPTY;
14959
14960   // this makes it possible to leave the removed element again
14961   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14962     Store[x][y] = element;
14963
14964   return TRUE;
14965 }
14966
14967 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14968 {
14969   int jx = player->jx, jy = player->jy;
14970   int x = jx + dx, y = jy + dy;
14971   int snap_direction = (dx == -1 ? MV_LEFT  :
14972                         dx == +1 ? MV_RIGHT :
14973                         dy == -1 ? MV_UP    :
14974                         dy == +1 ? MV_DOWN  : MV_NONE);
14975   boolean can_continue_snapping = (level.continuous_snapping &&
14976                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14977
14978   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14979     return FALSE;
14980
14981   if (!player->active || !IN_LEV_FIELD(x, y))
14982     return FALSE;
14983
14984   if (dx && dy)
14985     return FALSE;
14986
14987   if (!dx && !dy)
14988   {
14989     if (player->MovPos == 0)
14990       player->is_pushing = FALSE;
14991
14992     player->is_snapping = FALSE;
14993
14994     if (player->MovPos == 0)
14995     {
14996       player->is_moving = FALSE;
14997       player->is_digging = FALSE;
14998       player->is_collecting = FALSE;
14999     }
15000
15001     return FALSE;
15002   }
15003
15004   // prevent snapping with already pressed snap key when not allowed
15005   if (player->is_snapping && !can_continue_snapping)
15006     return FALSE;
15007
15008   player->MovDir = snap_direction;
15009
15010   if (player->MovPos == 0)
15011   {
15012     player->is_moving = FALSE;
15013     player->is_digging = FALSE;
15014     player->is_collecting = FALSE;
15015   }
15016
15017   player->is_dropping = FALSE;
15018   player->is_dropping_pressed = FALSE;
15019   player->drop_pressed_delay = 0;
15020
15021   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15022     return FALSE;
15023
15024   player->is_snapping = TRUE;
15025   player->is_active = TRUE;
15026
15027   if (player->MovPos == 0)
15028   {
15029     player->is_moving = FALSE;
15030     player->is_digging = FALSE;
15031     player->is_collecting = FALSE;
15032   }
15033
15034   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
15035     TEST_DrawLevelField(player->last_jx, player->last_jy);
15036
15037   TEST_DrawLevelField(x, y);
15038
15039   return TRUE;
15040 }
15041
15042 static boolean DropElement(struct PlayerInfo *player)
15043 {
15044   int old_element, new_element;
15045   int dropx = player->jx, dropy = player->jy;
15046   int drop_direction = player->MovDir;
15047   int drop_side = drop_direction;
15048   int drop_element = get_next_dropped_element(player);
15049
15050   /* do not drop an element on top of another element; when holding drop key
15051      pressed without moving, dropped element must move away before the next
15052      element can be dropped (this is especially important if the next element
15053      is dynamite, which can be placed on background for historical reasons) */
15054   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
15055     return MP_ACTION;
15056
15057   if (IS_THROWABLE(drop_element))
15058   {
15059     dropx += GET_DX_FROM_DIR(drop_direction);
15060     dropy += GET_DY_FROM_DIR(drop_direction);
15061
15062     if (!IN_LEV_FIELD(dropx, dropy))
15063       return FALSE;
15064   }
15065
15066   old_element = Tile[dropx][dropy];     // old element at dropping position
15067   new_element = drop_element;           // default: no change when dropping
15068
15069   // check if player is active, not moving and ready to drop
15070   if (!player->active || player->MovPos || player->drop_delay > 0)
15071     return FALSE;
15072
15073   // check if player has anything that can be dropped
15074   if (new_element == EL_UNDEFINED)
15075     return FALSE;
15076
15077   // only set if player has anything that can be dropped
15078   player->is_dropping_pressed = TRUE;
15079
15080   // check if drop key was pressed long enough for EM style dynamite
15081   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15082     return FALSE;
15083
15084   // check if anything can be dropped at the current position
15085   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15086     return FALSE;
15087
15088   // collected custom elements can only be dropped on empty fields
15089   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15090     return FALSE;
15091
15092   if (old_element != EL_EMPTY)
15093     Back[dropx][dropy] = old_element;   // store old element on this field
15094
15095   ResetGfxAnimation(dropx, dropy);
15096   ResetRandomAnimationValue(dropx, dropy);
15097
15098   if (player->inventory_size > 0 ||
15099       player->inventory_infinite_element != EL_UNDEFINED)
15100   {
15101     if (player->inventory_size > 0)
15102     {
15103       player->inventory_size--;
15104
15105       DrawGameDoorValues();
15106
15107       if (new_element == EL_DYNAMITE)
15108         new_element = EL_DYNAMITE_ACTIVE;
15109       else if (new_element == EL_EM_DYNAMITE)
15110         new_element = EL_EM_DYNAMITE_ACTIVE;
15111       else if (new_element == EL_SP_DISK_RED)
15112         new_element = EL_SP_DISK_RED_ACTIVE;
15113     }
15114
15115     Tile[dropx][dropy] = new_element;
15116
15117     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15118       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15119                           el2img(Tile[dropx][dropy]), 0);
15120
15121     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15122
15123     // needed if previous element just changed to "empty" in the last frame
15124     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15125
15126     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15127                                player->index_bit, drop_side);
15128     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15129                                         CE_PLAYER_DROPS_X,
15130                                         player->index_bit, drop_side);
15131
15132     TestIfElementTouchesCustomElement(dropx, dropy);
15133   }
15134   else          // player is dropping a dyna bomb
15135   {
15136     player->dynabombs_left--;
15137
15138     Tile[dropx][dropy] = new_element;
15139
15140     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15141       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15142                           el2img(Tile[dropx][dropy]), 0);
15143
15144     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15145   }
15146
15147   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15148     InitField_WithBug1(dropx, dropy, FALSE);
15149
15150   new_element = Tile[dropx][dropy];     // element might have changed
15151
15152   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15153       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15154   {
15155     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15156       MovDir[dropx][dropy] = drop_direction;
15157
15158     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15159
15160     // do not cause impact style collision by dropping elements that can fall
15161     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15162   }
15163
15164   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15165   player->is_dropping = TRUE;
15166
15167   player->drop_pressed_delay = 0;
15168   player->is_dropping_pressed = FALSE;
15169
15170   player->drop_x = dropx;
15171   player->drop_y = dropy;
15172
15173   return TRUE;
15174 }
15175
15176 // ----------------------------------------------------------------------------
15177 // game sound playing functions
15178 // ----------------------------------------------------------------------------
15179
15180 static int *loop_sound_frame = NULL;
15181 static int *loop_sound_volume = NULL;
15182
15183 void InitPlayLevelSound(void)
15184 {
15185   int num_sounds = getSoundListSize();
15186
15187   checked_free(loop_sound_frame);
15188   checked_free(loop_sound_volume);
15189
15190   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15191   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15192 }
15193
15194 static void PlayLevelSound(int x, int y, int nr)
15195 {
15196   int sx = SCREENX(x), sy = SCREENY(y);
15197   int volume, stereo_position;
15198   int max_distance = 8;
15199   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15200
15201   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15202       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15203     return;
15204
15205   if (!IN_LEV_FIELD(x, y) ||
15206       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15207       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15208     return;
15209
15210   volume = SOUND_MAX_VOLUME;
15211
15212   if (!IN_SCR_FIELD(sx, sy))
15213   {
15214     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15215     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15216
15217     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15218   }
15219
15220   stereo_position = (SOUND_MAX_LEFT +
15221                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15222                      (SCR_FIELDX + 2 * max_distance));
15223
15224   if (IS_LOOP_SOUND(nr))
15225   {
15226     /* This assures that quieter loop sounds do not overwrite louder ones,
15227        while restarting sound volume comparison with each new game frame. */
15228
15229     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15230       return;
15231
15232     loop_sound_volume[nr] = volume;
15233     loop_sound_frame[nr] = FrameCounter;
15234   }
15235
15236   PlaySoundExt(nr, volume, stereo_position, type);
15237 }
15238
15239 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15240 {
15241   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15242                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15243                  y < LEVELY(BY1) ? LEVELY(BY1) :
15244                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15245                  sound_action);
15246 }
15247
15248 static void PlayLevelSoundAction(int x, int y, int action)
15249 {
15250   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15251 }
15252
15253 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15254 {
15255   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15256
15257   if (sound_effect != SND_UNDEFINED)
15258     PlayLevelSound(x, y, sound_effect);
15259 }
15260
15261 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15262                                               int action)
15263 {
15264   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15265
15266   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15267     PlayLevelSound(x, y, sound_effect);
15268 }
15269
15270 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15271 {
15272   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15273
15274   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15275     PlayLevelSound(x, y, sound_effect);
15276 }
15277
15278 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15279 {
15280   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15281
15282   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15283     StopSound(sound_effect);
15284 }
15285
15286 static int getLevelMusicNr(void)
15287 {
15288   int level_pos = level_nr - leveldir_current->first_level;
15289
15290   if (levelset.music[level_nr] != MUS_UNDEFINED)
15291     return levelset.music[level_nr];            // from config file
15292   else
15293     return MAP_NOCONF_MUSIC(level_pos);         // from music dir
15294 }
15295
15296 static void FadeLevelSounds(void)
15297 {
15298   FadeSounds();
15299 }
15300
15301 static void FadeLevelMusic(void)
15302 {
15303   int music_nr = getLevelMusicNr();
15304   char *curr_music = getCurrentlyPlayingMusicFilename();
15305   char *next_music = getMusicInfoEntryFilename(music_nr);
15306
15307   if (!strEqual(curr_music, next_music))
15308     FadeMusic();
15309 }
15310
15311 void FadeLevelSoundsAndMusic(void)
15312 {
15313   FadeLevelSounds();
15314   FadeLevelMusic();
15315 }
15316
15317 static void PlayLevelMusic(void)
15318 {
15319   int music_nr = getLevelMusicNr();
15320   char *curr_music = getCurrentlyPlayingMusicFilename();
15321   char *next_music = getMusicInfoEntryFilename(music_nr);
15322
15323   if (!strEqual(curr_music, next_music))
15324     PlayMusicLoop(music_nr);
15325 }
15326
15327 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15328 {
15329   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15330   int offset = 0;
15331   int x = xx - offset;
15332   int y = yy - offset;
15333
15334   switch (sample)
15335   {
15336     case SOUND_blank:
15337       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15338       break;
15339
15340     case SOUND_roll:
15341       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15342       break;
15343
15344     case SOUND_stone:
15345       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15346       break;
15347
15348     case SOUND_nut:
15349       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15350       break;
15351
15352     case SOUND_crack:
15353       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15354       break;
15355
15356     case SOUND_bug:
15357       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15358       break;
15359
15360     case SOUND_tank:
15361       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15362       break;
15363
15364     case SOUND_android_clone:
15365       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15366       break;
15367
15368     case SOUND_android_move:
15369       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15370       break;
15371
15372     case SOUND_spring:
15373       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15374       break;
15375
15376     case SOUND_slurp:
15377       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15378       break;
15379
15380     case SOUND_eater:
15381       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15382       break;
15383
15384     case SOUND_eater_eat:
15385       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15386       break;
15387
15388     case SOUND_alien:
15389       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15390       break;
15391
15392     case SOUND_collect:
15393       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15394       break;
15395
15396     case SOUND_diamond:
15397       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15398       break;
15399
15400     case SOUND_squash:
15401       // !!! CHECK THIS !!!
15402 #if 1
15403       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15404 #else
15405       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15406 #endif
15407       break;
15408
15409     case SOUND_wonderfall:
15410       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15411       break;
15412
15413     case SOUND_drip:
15414       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15415       break;
15416
15417     case SOUND_push:
15418       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15419       break;
15420
15421     case SOUND_dirt:
15422       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15423       break;
15424
15425     case SOUND_acid:
15426       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15427       break;
15428
15429     case SOUND_ball:
15430       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15431       break;
15432
15433     case SOUND_slide:
15434       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15435       break;
15436
15437     case SOUND_wonder:
15438       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15439       break;
15440
15441     case SOUND_door:
15442       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15443       break;
15444
15445     case SOUND_exit_open:
15446       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15447       break;
15448
15449     case SOUND_exit_leave:
15450       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15451       break;
15452
15453     case SOUND_dynamite:
15454       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15455       break;
15456
15457     case SOUND_tick:
15458       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15459       break;
15460
15461     case SOUND_press:
15462       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15463       break;
15464
15465     case SOUND_wheel:
15466       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15467       break;
15468
15469     case SOUND_boom:
15470       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15471       break;
15472
15473     case SOUND_die:
15474       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15475       break;
15476
15477     case SOUND_time:
15478       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15479       break;
15480
15481     default:
15482       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15483       break;
15484   }
15485 }
15486
15487 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15488 {
15489   int element = map_element_SP_to_RND(element_sp);
15490   int action = map_action_SP_to_RND(action_sp);
15491   int offset = (setup.sp_show_border_elements ? 0 : 1);
15492   int x = xx - offset;
15493   int y = yy - offset;
15494
15495   PlayLevelSoundElementAction(x, y, element, action);
15496 }
15497
15498 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15499 {
15500   int element = map_element_MM_to_RND(element_mm);
15501   int action = map_action_MM_to_RND(action_mm);
15502   int offset = 0;
15503   int x = xx - offset;
15504   int y = yy - offset;
15505
15506   if (!IS_MM_ELEMENT(element))
15507     element = EL_MM_DEFAULT;
15508
15509   PlayLevelSoundElementAction(x, y, element, action);
15510 }
15511
15512 void PlaySound_MM(int sound_mm)
15513 {
15514   int sound = map_sound_MM_to_RND(sound_mm);
15515
15516   if (sound == SND_UNDEFINED)
15517     return;
15518
15519   PlaySound(sound);
15520 }
15521
15522 void PlaySoundLoop_MM(int sound_mm)
15523 {
15524   int sound = map_sound_MM_to_RND(sound_mm);
15525
15526   if (sound == SND_UNDEFINED)
15527     return;
15528
15529   PlaySoundLoop(sound);
15530 }
15531
15532 void StopSound_MM(int sound_mm)
15533 {
15534   int sound = map_sound_MM_to_RND(sound_mm);
15535
15536   if (sound == SND_UNDEFINED)
15537     return;
15538
15539   StopSound(sound);
15540 }
15541
15542 void RaiseScore(int value)
15543 {
15544   game.score += value;
15545
15546   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15547
15548   DisplayGameControlValues();
15549 }
15550
15551 void RaiseScoreElement(int element)
15552 {
15553   switch (element)
15554   {
15555     case EL_EMERALD:
15556     case EL_BD_DIAMOND:
15557     case EL_EMERALD_YELLOW:
15558     case EL_EMERALD_RED:
15559     case EL_EMERALD_PURPLE:
15560     case EL_SP_INFOTRON:
15561       RaiseScore(level.score[SC_EMERALD]);
15562       break;
15563     case EL_DIAMOND:
15564       RaiseScore(level.score[SC_DIAMOND]);
15565       break;
15566     case EL_CRYSTAL:
15567       RaiseScore(level.score[SC_CRYSTAL]);
15568       break;
15569     case EL_PEARL:
15570       RaiseScore(level.score[SC_PEARL]);
15571       break;
15572     case EL_BUG:
15573     case EL_BD_BUTTERFLY:
15574     case EL_SP_ELECTRON:
15575       RaiseScore(level.score[SC_BUG]);
15576       break;
15577     case EL_SPACESHIP:
15578     case EL_BD_FIREFLY:
15579     case EL_SP_SNIKSNAK:
15580       RaiseScore(level.score[SC_SPACESHIP]);
15581       break;
15582     case EL_YAMYAM:
15583     case EL_DARK_YAMYAM:
15584       RaiseScore(level.score[SC_YAMYAM]);
15585       break;
15586     case EL_ROBOT:
15587       RaiseScore(level.score[SC_ROBOT]);
15588       break;
15589     case EL_PACMAN:
15590       RaiseScore(level.score[SC_PACMAN]);
15591       break;
15592     case EL_NUT:
15593       RaiseScore(level.score[SC_NUT]);
15594       break;
15595     case EL_DYNAMITE:
15596     case EL_EM_DYNAMITE:
15597     case EL_SP_DISK_RED:
15598     case EL_DYNABOMB_INCREASE_NUMBER:
15599     case EL_DYNABOMB_INCREASE_SIZE:
15600     case EL_DYNABOMB_INCREASE_POWER:
15601       RaiseScore(level.score[SC_DYNAMITE]);
15602       break;
15603     case EL_SHIELD_NORMAL:
15604     case EL_SHIELD_DEADLY:
15605       RaiseScore(level.score[SC_SHIELD]);
15606       break;
15607     case EL_EXTRA_TIME:
15608       RaiseScore(level.extra_time_score);
15609       break;
15610     case EL_KEY_1:
15611     case EL_KEY_2:
15612     case EL_KEY_3:
15613     case EL_KEY_4:
15614     case EL_EM_KEY_1:
15615     case EL_EM_KEY_2:
15616     case EL_EM_KEY_3:
15617     case EL_EM_KEY_4:
15618     case EL_EMC_KEY_5:
15619     case EL_EMC_KEY_6:
15620     case EL_EMC_KEY_7:
15621     case EL_EMC_KEY_8:
15622     case EL_DC_KEY_WHITE:
15623       RaiseScore(level.score[SC_KEY]);
15624       break;
15625     default:
15626       RaiseScore(element_info[element].collect_score);
15627       break;
15628   }
15629 }
15630
15631 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15632 {
15633   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15634   {
15635     if (!quick_quit)
15636     {
15637       // prevent short reactivation of overlay buttons while closing door
15638       SetOverlayActive(FALSE);
15639       UnmapGameButtons();
15640
15641       // door may still be open due to skipped or envelope style request
15642       CloseDoor(score_info_tape_play ? DOOR_CLOSE_ALL : DOOR_CLOSE_1);
15643     }
15644
15645     if (network.enabled)
15646       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15647     else
15648     {
15649       if (quick_quit)
15650         FadeSkipNextFadeIn();
15651
15652       SetGameStatus(GAME_MODE_MAIN);
15653
15654       DrawMainMenu();
15655     }
15656   }
15657   else          // continue playing the game
15658   {
15659     if (tape.playing && tape.deactivate_display)
15660       TapeDeactivateDisplayOff(TRUE);
15661
15662     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15663
15664     if (tape.playing && tape.deactivate_display)
15665       TapeDeactivateDisplayOn();
15666   }
15667 }
15668
15669 void RequestQuitGame(boolean escape_key_pressed)
15670 {
15671   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15672   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15673                         level_editor_test_game);
15674   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15675                           quick_quit || score_info_tape_play);
15676
15677   RequestQuitGameExt(skip_request, quick_quit,
15678                      "Do you really want to quit the game?");
15679 }
15680
15681 static char *getRestartGameMessage(void)
15682 {
15683   boolean play_again = hasStartedNetworkGame();
15684   static char message[MAX_OUTPUT_LINESIZE];
15685   char *game_over_text = "Game over!";
15686   char *play_again_text = " Play it again?";
15687
15688   if (level.game_engine_type == GAME_ENGINE_TYPE_MM &&
15689       game_mm.game_over_message != NULL)
15690     game_over_text = game_mm.game_over_message;
15691
15692   snprintf(message, MAX_OUTPUT_LINESIZE, "%s%s", game_over_text,
15693            (play_again ? play_again_text : ""));
15694
15695   return message;
15696 }
15697
15698 static void RequestRestartGame(void)
15699 {
15700   char *message = getRestartGameMessage();
15701   boolean has_started_game = hasStartedNetworkGame();
15702   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15703   int door_state = DOOR_CLOSE_1;
15704
15705   if (Request(message, request_mode | REQ_STAY_OPEN) && has_started_game)
15706   {
15707     CloseDoor(door_state);
15708
15709     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15710   }
15711   else
15712   {
15713     // if game was invoked from level editor, also close tape recorder door
15714     if (level_editor_test_game)
15715       door_state = DOOR_CLOSE_ALL;
15716
15717     CloseDoor(door_state);
15718
15719     SetGameStatus(GAME_MODE_MAIN);
15720
15721     DrawMainMenu();
15722   }
15723 }
15724
15725 boolean CheckRestartGame(void)
15726 {
15727   static int game_over_delay = 0;
15728   int game_over_delay_value = 50;
15729   boolean game_over = checkGameFailed();
15730
15731   if (!game_over)
15732   {
15733     game_over_delay = game_over_delay_value;
15734
15735     return FALSE;
15736   }
15737
15738   if (game_over_delay > 0)
15739   {
15740     if (game_over_delay == game_over_delay_value / 2)
15741       PlaySound(SND_GAME_LOSING);
15742
15743     game_over_delay--;
15744
15745     return FALSE;
15746   }
15747
15748   // do not handle game over if request dialog is already active
15749   if (game.request_active)
15750     return FALSE;
15751
15752   // do not ask to play again if game was never actually played
15753   if (!game.GamePlayed)
15754     return FALSE;
15755
15756   // do not ask to play again if this was disabled in setup menu
15757   if (!setup.ask_on_game_over)
15758     return FALSE;
15759
15760   RequestRestartGame();
15761
15762   return TRUE;
15763 }
15764
15765 boolean checkGameSolved(void)
15766 {
15767   // set for all game engines if level was solved
15768   return game.LevelSolved_GameEnd;
15769 }
15770
15771 boolean checkGameFailed(void)
15772 {
15773   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15774     return (game_em.game_over && !game_em.level_solved);
15775   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15776     return (game_sp.game_over && !game_sp.level_solved);
15777   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15778     return (game_mm.game_over && !game_mm.level_solved);
15779   else                          // GAME_ENGINE_TYPE_RND
15780     return (game.GameOver && !game.LevelSolved);
15781 }
15782
15783 boolean checkGameEnded(void)
15784 {
15785   return (checkGameSolved() || checkGameFailed());
15786 }
15787
15788
15789 // ----------------------------------------------------------------------------
15790 // random generator functions
15791 // ----------------------------------------------------------------------------
15792
15793 unsigned int InitEngineRandom_RND(int seed)
15794 {
15795   game.num_random_calls = 0;
15796
15797   return InitEngineRandom(seed);
15798 }
15799
15800 unsigned int RND(int max)
15801 {
15802   if (max > 0)
15803   {
15804     game.num_random_calls++;
15805
15806     return GetEngineRandom(max);
15807   }
15808
15809   return 0;
15810 }
15811
15812
15813 // ----------------------------------------------------------------------------
15814 // game engine snapshot handling functions
15815 // ----------------------------------------------------------------------------
15816
15817 struct EngineSnapshotInfo
15818 {
15819   // runtime values for custom element collect score
15820   int collect_score[NUM_CUSTOM_ELEMENTS];
15821
15822   // runtime values for group element choice position
15823   int choice_pos[NUM_GROUP_ELEMENTS];
15824
15825   // runtime values for belt position animations
15826   int belt_graphic[4][NUM_BELT_PARTS];
15827   int belt_anim_mode[4][NUM_BELT_PARTS];
15828 };
15829
15830 static struct EngineSnapshotInfo engine_snapshot_rnd;
15831 static char *snapshot_level_identifier = NULL;
15832 static int snapshot_level_nr = -1;
15833
15834 static void SaveEngineSnapshotValues_RND(void)
15835 {
15836   static int belt_base_active_element[4] =
15837   {
15838     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15839     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15840     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15841     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15842   };
15843   int i, j;
15844
15845   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15846   {
15847     int element = EL_CUSTOM_START + i;
15848
15849     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15850   }
15851
15852   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15853   {
15854     int element = EL_GROUP_START + i;
15855
15856     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15857   }
15858
15859   for (i = 0; i < 4; i++)
15860   {
15861     for (j = 0; j < NUM_BELT_PARTS; j++)
15862     {
15863       int element = belt_base_active_element[i] + j;
15864       int graphic = el2img(element);
15865       int anim_mode = graphic_info[graphic].anim_mode;
15866
15867       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15868       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15869     }
15870   }
15871 }
15872
15873 static void LoadEngineSnapshotValues_RND(void)
15874 {
15875   unsigned int num_random_calls = game.num_random_calls;
15876   int i, j;
15877
15878   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15879   {
15880     int element = EL_CUSTOM_START + i;
15881
15882     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15883   }
15884
15885   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15886   {
15887     int element = EL_GROUP_START + i;
15888
15889     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15890   }
15891
15892   for (i = 0; i < 4; i++)
15893   {
15894     for (j = 0; j < NUM_BELT_PARTS; j++)
15895     {
15896       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15897       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15898
15899       graphic_info[graphic].anim_mode = anim_mode;
15900     }
15901   }
15902
15903   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15904   {
15905     InitRND(tape.random_seed);
15906     for (i = 0; i < num_random_calls; i++)
15907       RND(1);
15908   }
15909
15910   if (game.num_random_calls != num_random_calls)
15911   {
15912     Error("number of random calls out of sync");
15913     Error("number of random calls should be %d", num_random_calls);
15914     Error("number of random calls is %d", game.num_random_calls);
15915
15916     Fail("this should not happen -- please debug");
15917   }
15918 }
15919
15920 void FreeEngineSnapshotSingle(void)
15921 {
15922   FreeSnapshotSingle();
15923
15924   setString(&snapshot_level_identifier, NULL);
15925   snapshot_level_nr = -1;
15926 }
15927
15928 void FreeEngineSnapshotList(void)
15929 {
15930   FreeSnapshotList();
15931 }
15932
15933 static ListNode *SaveEngineSnapshotBuffers(void)
15934 {
15935   ListNode *buffers = NULL;
15936
15937   // copy some special values to a structure better suited for the snapshot
15938
15939   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15940     SaveEngineSnapshotValues_RND();
15941   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15942     SaveEngineSnapshotValues_EM();
15943   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15944     SaveEngineSnapshotValues_SP(&buffers);
15945   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15946     SaveEngineSnapshotValues_MM();
15947
15948   // save values stored in special snapshot structure
15949
15950   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15951     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15952   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15953     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15954   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15955     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15956   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15957     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15958
15959   // save further RND engine values
15960
15961   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15962   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15963   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15964
15965   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15966   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15967   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15968   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15969   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15970
15971   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15972   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15973   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15974
15975   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15976
15977   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15978   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15979
15980   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15981   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15982   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15983   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15984   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15985   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15986   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15987   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15988   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15989   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15990   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15991   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15992   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15993   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15994   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15995   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15996   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15997   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15998
15999   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16000   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16001
16002   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16003   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16004   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16005
16006   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16007   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16008
16009   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16010   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16011   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
16012   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16013   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16014   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16015
16016   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16017   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16018
16019 #if 0
16020   ListNode *node = engine_snapshot_list_rnd;
16021   int num_bytes = 0;
16022
16023   while (node != NULL)
16024   {
16025     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16026
16027     node = node->next;
16028   }
16029
16030   Debug("game:playing:SaveEngineSnapshotBuffers",
16031         "size of engine snapshot: %d bytes", num_bytes);
16032 #endif
16033
16034   return buffers;
16035 }
16036
16037 void SaveEngineSnapshotSingle(void)
16038 {
16039   ListNode *buffers = SaveEngineSnapshotBuffers();
16040
16041   // finally save all snapshot buffers to single snapshot
16042   SaveSnapshotSingle(buffers);
16043
16044   // save level identification information
16045   setString(&snapshot_level_identifier, leveldir_current->identifier);
16046   snapshot_level_nr = level_nr;
16047 }
16048
16049 boolean CheckSaveEngineSnapshotToList(void)
16050 {
16051   boolean save_snapshot =
16052     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
16053      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
16054       game.snapshot.changed_action) ||
16055      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16056       game.snapshot.collected_item));
16057
16058   game.snapshot.changed_action = FALSE;
16059   game.snapshot.collected_item = FALSE;
16060   game.snapshot.save_snapshot = save_snapshot;
16061
16062   return save_snapshot;
16063 }
16064
16065 void SaveEngineSnapshotToList(void)
16066 {
16067   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
16068       tape.quick_resume)
16069     return;
16070
16071   ListNode *buffers = SaveEngineSnapshotBuffers();
16072
16073   // finally save all snapshot buffers to snapshot list
16074   SaveSnapshotToList(buffers);
16075 }
16076
16077 void SaveEngineSnapshotToListInitial(void)
16078 {
16079   FreeEngineSnapshotList();
16080
16081   SaveEngineSnapshotToList();
16082 }
16083
16084 static void LoadEngineSnapshotValues(void)
16085 {
16086   // restore special values from snapshot structure
16087
16088   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16089     LoadEngineSnapshotValues_RND();
16090   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16091     LoadEngineSnapshotValues_EM();
16092   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16093     LoadEngineSnapshotValues_SP();
16094   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16095     LoadEngineSnapshotValues_MM();
16096 }
16097
16098 void LoadEngineSnapshotSingle(void)
16099 {
16100   LoadSnapshotSingle();
16101
16102   LoadEngineSnapshotValues();
16103 }
16104
16105 static void LoadEngineSnapshot_Undo(int steps)
16106 {
16107   LoadSnapshotFromList_Older(steps);
16108
16109   LoadEngineSnapshotValues();
16110 }
16111
16112 static void LoadEngineSnapshot_Redo(int steps)
16113 {
16114   LoadSnapshotFromList_Newer(steps);
16115
16116   LoadEngineSnapshotValues();
16117 }
16118
16119 boolean CheckEngineSnapshotSingle(void)
16120 {
16121   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16122           snapshot_level_nr == level_nr);
16123 }
16124
16125 boolean CheckEngineSnapshotList(void)
16126 {
16127   return CheckSnapshotList();
16128 }
16129
16130
16131 // ---------- new game button stuff -------------------------------------------
16132
16133 static struct
16134 {
16135   int graphic;
16136   struct XY *pos;
16137   int gadget_id;
16138   boolean *setup_value;
16139   boolean allowed_on_tape;
16140   boolean is_touch_button;
16141   char *infotext;
16142 } gamebutton_info[NUM_GAME_BUTTONS] =
16143 {
16144   {
16145     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16146     GAME_CTRL_ID_STOP,                          NULL,
16147     TRUE, FALSE,                                "stop game"
16148   },
16149   {
16150     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16151     GAME_CTRL_ID_PAUSE,                         NULL,
16152     TRUE, FALSE,                                "pause game"
16153   },
16154   {
16155     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16156     GAME_CTRL_ID_PLAY,                          NULL,
16157     TRUE, FALSE,                                "play game"
16158   },
16159   {
16160     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16161     GAME_CTRL_ID_UNDO,                          NULL,
16162     TRUE, FALSE,                                "undo step"
16163   },
16164   {
16165     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16166     GAME_CTRL_ID_REDO,                          NULL,
16167     TRUE, FALSE,                                "redo step"
16168   },
16169   {
16170     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16171     GAME_CTRL_ID_SAVE,                          NULL,
16172     TRUE, FALSE,                                "save game"
16173   },
16174   {
16175     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16176     GAME_CTRL_ID_PAUSE2,                        NULL,
16177     TRUE, FALSE,                                "pause game"
16178   },
16179   {
16180     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16181     GAME_CTRL_ID_LOAD,                          NULL,
16182     TRUE, FALSE,                                "load game"
16183   },
16184   {
16185     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16186     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16187     FALSE, FALSE,                               "stop game"
16188   },
16189   {
16190     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16191     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16192     FALSE, FALSE,                               "pause game"
16193   },
16194   {
16195     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16196     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16197     FALSE, FALSE,                               "play game"
16198   },
16199   {
16200     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16201     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16202     FALSE, TRUE,                                "stop game"
16203   },
16204   {
16205     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16206     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16207     FALSE, TRUE,                                "pause game"
16208   },
16209   {
16210     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16211     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16212     TRUE, FALSE,                                "background music on/off"
16213   },
16214   {
16215     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16216     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16217     TRUE, FALSE,                                "sound loops on/off"
16218   },
16219   {
16220     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16221     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16222     TRUE, FALSE,                                "normal sounds on/off"
16223   },
16224   {
16225     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16226     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16227     FALSE, FALSE,                               "background music on/off"
16228   },
16229   {
16230     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16231     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16232     FALSE, FALSE,                               "sound loops on/off"
16233   },
16234   {
16235     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16236     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16237     FALSE, FALSE,                               "normal sounds on/off"
16238   }
16239 };
16240
16241 void CreateGameButtons(void)
16242 {
16243   int i;
16244
16245   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16246   {
16247     int graphic = gamebutton_info[i].graphic;
16248     struct GraphicInfo *gfx = &graphic_info[graphic];
16249     struct XY *pos = gamebutton_info[i].pos;
16250     struct GadgetInfo *gi;
16251     int button_type;
16252     boolean checked;
16253     unsigned int event_mask;
16254     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16255     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16256     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16257     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16258     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16259     int gd_x   = gfx->src_x;
16260     int gd_y   = gfx->src_y;
16261     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16262     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16263     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16264     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16265     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16266     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16267     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16268     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16269     int id = i;
16270
16271     // do not use touch buttons if overlay touch buttons are disabled
16272     if (is_touch_button && !setup.touch.overlay_buttons)
16273       continue;
16274
16275     if (gfx->bitmap == NULL)
16276     {
16277       game_gadget[id] = NULL;
16278
16279       continue;
16280     }
16281
16282     if (id == GAME_CTRL_ID_STOP ||
16283         id == GAME_CTRL_ID_PANEL_STOP ||
16284         id == GAME_CTRL_ID_TOUCH_STOP ||
16285         id == GAME_CTRL_ID_PLAY ||
16286         id == GAME_CTRL_ID_PANEL_PLAY ||
16287         id == GAME_CTRL_ID_SAVE ||
16288         id == GAME_CTRL_ID_LOAD)
16289     {
16290       button_type = GD_TYPE_NORMAL_BUTTON;
16291       checked = FALSE;
16292       event_mask = GD_EVENT_RELEASED;
16293     }
16294     else if (id == GAME_CTRL_ID_UNDO ||
16295              id == GAME_CTRL_ID_REDO)
16296     {
16297       button_type = GD_TYPE_NORMAL_BUTTON;
16298       checked = FALSE;
16299       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16300     }
16301     else
16302     {
16303       button_type = GD_TYPE_CHECK_BUTTON;
16304       checked = (gamebutton_info[i].setup_value != NULL ?
16305                  *gamebutton_info[i].setup_value : FALSE);
16306       event_mask = GD_EVENT_PRESSED;
16307     }
16308
16309     gi = CreateGadget(GDI_CUSTOM_ID, id,
16310                       GDI_IMAGE_ID, graphic,
16311                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16312                       GDI_X, base_x + x,
16313                       GDI_Y, base_y + y,
16314                       GDI_WIDTH, gfx->width,
16315                       GDI_HEIGHT, gfx->height,
16316                       GDI_TYPE, button_type,
16317                       GDI_STATE, GD_BUTTON_UNPRESSED,
16318                       GDI_CHECKED, checked,
16319                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16320                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16321                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16322                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16323                       GDI_DIRECT_DRAW, FALSE,
16324                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16325                       GDI_EVENT_MASK, event_mask,
16326                       GDI_CALLBACK_ACTION, HandleGameButtons,
16327                       GDI_END);
16328
16329     if (gi == NULL)
16330       Fail("cannot create gadget");
16331
16332     game_gadget[id] = gi;
16333   }
16334 }
16335
16336 void FreeGameButtons(void)
16337 {
16338   int i;
16339
16340   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16341     FreeGadget(game_gadget[i]);
16342 }
16343
16344 static void UnmapGameButtonsAtSamePosition(int id)
16345 {
16346   int i;
16347
16348   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16349     if (i != id &&
16350         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16351         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16352       UnmapGadget(game_gadget[i]);
16353 }
16354
16355 static void UnmapGameButtonsAtSamePosition_All(void)
16356 {
16357   if (setup.show_load_save_buttons)
16358   {
16359     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16360     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16361     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16362   }
16363   else if (setup.show_undo_redo_buttons)
16364   {
16365     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16366     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16367     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16368   }
16369   else
16370   {
16371     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16372     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16373     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16374
16375     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16376     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16377     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16378   }
16379 }
16380
16381 void MapLoadSaveButtons(void)
16382 {
16383   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16384   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16385
16386   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
16387   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
16388 }
16389
16390 void MapUndoRedoButtons(void)
16391 {
16392   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16393   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16394
16395   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16396   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16397 }
16398
16399 void ModifyPauseButtons(void)
16400 {
16401   static int ids[] =
16402   {
16403     GAME_CTRL_ID_PAUSE,
16404     GAME_CTRL_ID_PAUSE2,
16405     GAME_CTRL_ID_PANEL_PAUSE,
16406     GAME_CTRL_ID_TOUCH_PAUSE,
16407     -1
16408   };
16409   int i;
16410
16411   for (i = 0; ids[i] > -1; i++)
16412     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16413 }
16414
16415 static void MapGameButtonsExt(boolean on_tape)
16416 {
16417   int i;
16418
16419   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16420   {
16421     if ((i == GAME_CTRL_ID_UNDO ||
16422          i == GAME_CTRL_ID_REDO) &&
16423         game_status != GAME_MODE_PLAYING)
16424       continue;
16425
16426     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16427       MapGadget(game_gadget[i]);
16428   }
16429
16430   UnmapGameButtonsAtSamePosition_All();
16431
16432   RedrawGameButtons();
16433 }
16434
16435 static void UnmapGameButtonsExt(boolean on_tape)
16436 {
16437   int i;
16438
16439   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16440     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16441       UnmapGadget(game_gadget[i]);
16442 }
16443
16444 static void RedrawGameButtonsExt(boolean on_tape)
16445 {
16446   int i;
16447
16448   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16449     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16450       RedrawGadget(game_gadget[i]);
16451 }
16452
16453 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16454 {
16455   if (gi == NULL)
16456     return;
16457
16458   gi->checked = state;
16459 }
16460
16461 static void RedrawSoundButtonGadget(int id)
16462 {
16463   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16464              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16465              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16466              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16467              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16468              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16469              id);
16470
16471   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16472   RedrawGadget(game_gadget[id2]);
16473 }
16474
16475 void MapGameButtons(void)
16476 {
16477   MapGameButtonsExt(FALSE);
16478 }
16479
16480 void UnmapGameButtons(void)
16481 {
16482   UnmapGameButtonsExt(FALSE);
16483 }
16484
16485 void RedrawGameButtons(void)
16486 {
16487   RedrawGameButtonsExt(FALSE);
16488 }
16489
16490 void MapGameButtonsOnTape(void)
16491 {
16492   MapGameButtonsExt(TRUE);
16493 }
16494
16495 void UnmapGameButtonsOnTape(void)
16496 {
16497   UnmapGameButtonsExt(TRUE);
16498 }
16499
16500 void RedrawGameButtonsOnTape(void)
16501 {
16502   RedrawGameButtonsExt(TRUE);
16503 }
16504
16505 static void GameUndoRedoExt(void)
16506 {
16507   ClearPlayerAction();
16508
16509   tape.pausing = TRUE;
16510
16511   RedrawPlayfield();
16512   UpdateAndDisplayGameControlValues();
16513
16514   DrawCompleteVideoDisplay();
16515   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16516   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16517   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16518
16519   ModifyPauseButtons();
16520
16521   BackToFront();
16522 }
16523
16524 static void GameUndo(int steps)
16525 {
16526   if (!CheckEngineSnapshotList())
16527     return;
16528
16529   int tape_property_bits = tape.property_bits;
16530
16531   LoadEngineSnapshot_Undo(steps);
16532
16533   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16534
16535   GameUndoRedoExt();
16536 }
16537
16538 static void GameRedo(int steps)
16539 {
16540   if (!CheckEngineSnapshotList())
16541     return;
16542
16543   int tape_property_bits = tape.property_bits;
16544
16545   LoadEngineSnapshot_Redo(steps);
16546
16547   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16548
16549   GameUndoRedoExt();
16550 }
16551
16552 static void HandleGameButtonsExt(int id, int button)
16553 {
16554   static boolean game_undo_executed = FALSE;
16555   int steps = BUTTON_STEPSIZE(button);
16556   boolean handle_game_buttons =
16557     (game_status == GAME_MODE_PLAYING ||
16558      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16559
16560   if (!handle_game_buttons)
16561     return;
16562
16563   switch (id)
16564   {
16565     case GAME_CTRL_ID_STOP:
16566     case GAME_CTRL_ID_PANEL_STOP:
16567     case GAME_CTRL_ID_TOUCH_STOP:
16568       TapeStopGame();
16569
16570       break;
16571
16572     case GAME_CTRL_ID_PAUSE:
16573     case GAME_CTRL_ID_PAUSE2:
16574     case GAME_CTRL_ID_PANEL_PAUSE:
16575     case GAME_CTRL_ID_TOUCH_PAUSE:
16576       if (network.enabled && game_status == GAME_MODE_PLAYING)
16577       {
16578         if (tape.pausing)
16579           SendToServer_ContinuePlaying();
16580         else
16581           SendToServer_PausePlaying();
16582       }
16583       else
16584         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16585
16586       game_undo_executed = FALSE;
16587
16588       break;
16589
16590     case GAME_CTRL_ID_PLAY:
16591     case GAME_CTRL_ID_PANEL_PLAY:
16592       if (game_status == GAME_MODE_MAIN)
16593       {
16594         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16595       }
16596       else if (tape.pausing)
16597       {
16598         if (network.enabled)
16599           SendToServer_ContinuePlaying();
16600         else
16601           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16602       }
16603       break;
16604
16605     case GAME_CTRL_ID_UNDO:
16606       // Important: When using "save snapshot when collecting an item" mode,
16607       // load last (current) snapshot for first "undo" after pressing "pause"
16608       // (else the last-but-one snapshot would be loaded, because the snapshot
16609       // pointer already points to the last snapshot when pressing "pause",
16610       // which is fine for "every step/move" mode, but not for "every collect")
16611       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16612           !game_undo_executed)
16613         steps--;
16614
16615       game_undo_executed = TRUE;
16616
16617       GameUndo(steps);
16618       break;
16619
16620     case GAME_CTRL_ID_REDO:
16621       GameRedo(steps);
16622       break;
16623
16624     case GAME_CTRL_ID_SAVE:
16625       TapeQuickSave();
16626       break;
16627
16628     case GAME_CTRL_ID_LOAD:
16629       TapeQuickLoad();
16630       break;
16631
16632     case SOUND_CTRL_ID_MUSIC:
16633     case SOUND_CTRL_ID_PANEL_MUSIC:
16634       if (setup.sound_music)
16635       { 
16636         setup.sound_music = FALSE;
16637
16638         FadeMusic();
16639       }
16640       else if (audio.music_available)
16641       { 
16642         setup.sound = setup.sound_music = TRUE;
16643
16644         SetAudioMode(setup.sound);
16645
16646         if (game_status == GAME_MODE_PLAYING)
16647           PlayLevelMusic();
16648       }
16649
16650       RedrawSoundButtonGadget(id);
16651
16652       break;
16653
16654     case SOUND_CTRL_ID_LOOPS:
16655     case SOUND_CTRL_ID_PANEL_LOOPS:
16656       if (setup.sound_loops)
16657         setup.sound_loops = FALSE;
16658       else if (audio.loops_available)
16659       {
16660         setup.sound = setup.sound_loops = TRUE;
16661
16662         SetAudioMode(setup.sound);
16663       }
16664
16665       RedrawSoundButtonGadget(id);
16666
16667       break;
16668
16669     case SOUND_CTRL_ID_SIMPLE:
16670     case SOUND_CTRL_ID_PANEL_SIMPLE:
16671       if (setup.sound_simple)
16672         setup.sound_simple = FALSE;
16673       else if (audio.sound_available)
16674       {
16675         setup.sound = setup.sound_simple = TRUE;
16676
16677         SetAudioMode(setup.sound);
16678       }
16679
16680       RedrawSoundButtonGadget(id);
16681
16682       break;
16683
16684     default:
16685       break;
16686   }
16687 }
16688
16689 static void HandleGameButtons(struct GadgetInfo *gi)
16690 {
16691   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16692 }
16693
16694 void HandleSoundButtonKeys(Key key)
16695 {
16696   if (key == setup.shortcut.sound_simple)
16697     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16698   else if (key == setup.shortcut.sound_loops)
16699     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16700   else if (key == setup.shortcut.sound_music)
16701     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16702 }