fixed broken animation of CE that can not change, but has CE delay action
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 // DEBUG SETTINGS
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 // EXPERIMENTAL STUFF
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 // EXPERIMENTAL STUFF
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 // for DigField()
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 // for MovePlayer()
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 // for ScrollPlayer()
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 // for Bang()/Explode()
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 // game panel display and control definitions
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 // values for delayed check of falling and moving elements and for collision
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 // values for initial player move delay (initial delay counter value)
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 // values for player movement speed (which is in fact a delay value)
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 // values for scroll positions
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 // values for other actions
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
883                                  RND(element_info[e].step_delay_random))
884 #define GET_MAX_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
885                                     (element_info[e].step_delay_random))
886 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
887                                  RND(element_info[e].ce_value_random_initial))
888 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
889 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
890                                  RND((c)->delay_random * (c)->delay_frames))
891 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
892                                  RND((c)->delay_random))
893
894
895 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
896          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
897
898 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
899         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
900          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
901          (be) + (e) - EL_SELF)
902
903 #define GET_PLAYER_FROM_BITS(p)                                         \
904         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
905
906 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
907         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
908          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
909          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
910          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
911          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
912          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
913          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
914          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
915          (e))
916
917 #define CAN_GROW_INTO(e)                                                \
918         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (condition)))
923
924 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
925                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
926                                         (CAN_MOVE_INTO_ACID(e) &&       \
927                                          Tile[x][y] == EL_ACID) ||      \
928                                         (condition)))
929
930 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
931                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
932                                         (CAN_MOVE_INTO_ACID(e) &&       \
933                                          Tile[x][y] == EL_ACID) ||      \
934                                         (condition)))
935
936 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
937                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
938                                         (condition) ||                  \
939                                         (CAN_MOVE_INTO_ACID(e) &&       \
940                                          Tile[x][y] == EL_ACID) ||      \
941                                         (DONT_COLLIDE_WITH(e) &&        \
942                                          IS_PLAYER(x, y) &&             \
943                                          !PLAYER_ENEMY_PROTECTED(x, y))))
944
945 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
946         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
947
948 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
949         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
950
951 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
952         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Tile[x][y] == EL_EMC_PLANT)
953
954 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
955         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Tile[x][y]) || \
956                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
957
958 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
959         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
960
961 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
962         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Tile[x][y] == EL_DIAMOND)
963
964 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
965         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_FOOD_DARK_YAMYAM(Tile[x][y]))
966
967 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
968         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Tile[x][y]))
969
970 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
971         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Tile[x][y]))
972
973 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
974         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Tile[x][y] == EL_EXIT_OPEN || \
975                                                  Tile[x][y] == EL_EM_EXIT_OPEN || \
976                                                  Tile[x][y] == EL_STEEL_EXIT_OPEN || \
977                                                  Tile[x][y] == EL_EM_STEEL_EXIT_OPEN || \
978                                                  IS_FOOD_PENGUIN(Tile[x][y])))
979 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
980         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
981
982 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
983         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
984
985 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
986         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
987
988 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
989         (IN_LEV_FIELD(x, y) && (Tile[x][y] == EL_EMC_SPRING_BUMPER ||   \
990                                 Tile[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
991
992 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
993
994 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
995                 (!IS_PLAYER(x, y) &&                                    \
996                  IS_EQUAL_OR_IN_GROUP(Tile[x][y], MOVE_ENTER_EL(e)))
997
998 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
999         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1000
1001 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1002 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1003
1004 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1005 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1006 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1007 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1008
1009 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1010
1011 // game button identifiers
1012 #define GAME_CTRL_ID_STOP               0
1013 #define GAME_CTRL_ID_PAUSE              1
1014 #define GAME_CTRL_ID_PLAY               2
1015 #define GAME_CTRL_ID_UNDO               3
1016 #define GAME_CTRL_ID_REDO               4
1017 #define GAME_CTRL_ID_SAVE               5
1018 #define GAME_CTRL_ID_PAUSE2             6
1019 #define GAME_CTRL_ID_LOAD               7
1020 #define GAME_CTRL_ID_PANEL_STOP         8
1021 #define GAME_CTRL_ID_PANEL_PAUSE        9
1022 #define GAME_CTRL_ID_PANEL_PLAY         10
1023 #define GAME_CTRL_ID_TOUCH_STOP         11
1024 #define GAME_CTRL_ID_TOUCH_PAUSE        12
1025 #define SOUND_CTRL_ID_MUSIC             13
1026 #define SOUND_CTRL_ID_LOOPS             14
1027 #define SOUND_CTRL_ID_SIMPLE            15
1028 #define SOUND_CTRL_ID_PANEL_MUSIC       16
1029 #define SOUND_CTRL_ID_PANEL_LOOPS       17
1030 #define SOUND_CTRL_ID_PANEL_SIMPLE      18
1031
1032 #define NUM_GAME_BUTTONS                19
1033
1034
1035 // forward declaration for internal use
1036
1037 static void CreateField(int, int, int);
1038
1039 static void ResetGfxAnimation(int, int);
1040
1041 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1042 static void AdvanceFrameAndPlayerCounters(int);
1043
1044 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1045 static boolean MovePlayer(struct PlayerInfo *, int, int);
1046 static void ScrollPlayer(struct PlayerInfo *, int);
1047 static void ScrollScreen(struct PlayerInfo *, int);
1048
1049 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1050 static boolean DigFieldByCE(int, int, int);
1051 static boolean SnapField(struct PlayerInfo *, int, int);
1052 static boolean DropElement(struct PlayerInfo *);
1053
1054 static void InitBeltMovement(void);
1055 static void CloseAllOpenTimegates(void);
1056 static void CheckGravityMovement(struct PlayerInfo *);
1057 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1058 static void KillPlayerUnlessEnemyProtected(int, int);
1059 static void KillPlayerUnlessExplosionProtected(int, int);
1060
1061 static void CheckNextToConditions(int, int);
1062 static void TestIfPlayerNextToCustomElement(int, int);
1063 static void TestIfPlayerTouchesCustomElement(int, int);
1064 static void TestIfElementNextToCustomElement(int, int);
1065 static void TestIfElementTouchesCustomElement(int, int);
1066 static void TestIfElementHitsCustomElement(int, int, int);
1067
1068 static void HandleElementChange(int, int, int);
1069 static void ExecuteCustomElementAction(int, int, int, int);
1070 static boolean ChangeElement(int, int, int, int);
1071
1072 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int, int, int);
1073 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1074         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
1075 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1076         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1077 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1078         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1079 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1080         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1081 #define CheckTriggeredElementChangeByMouse(x, y, e, ev, s)              \
1082         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1083
1084 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1085 #define CheckElementChange(x, y, e, te, ev)                             \
1086         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1087 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1088         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1089 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1090         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1091 #define CheckElementChangeByMouse(x, y, e, ev, s)                       \
1092         CheckElementChangeExt(x, y, e, EL_UNDEFINED, ev, CH_PLAYER_ANY, s)
1093
1094 static void PlayLevelSound(int, int, int);
1095 static void PlayLevelSoundNearest(int, int, int);
1096 static void PlayLevelSoundAction(int, int, int);
1097 static void PlayLevelSoundElementAction(int, int, int, int);
1098 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1099 static void PlayLevelSoundActionIfLoop(int, int, int);
1100 static void StopLevelSoundActionIfLoop(int, int, int);
1101 static void PlayLevelMusic(void);
1102 static void FadeLevelSoundsAndMusic(void);
1103
1104 static void HandleGameButtons(struct GadgetInfo *);
1105
1106 int AmoebaNeighbourNr(int, int);
1107 void AmoebaToDiamond(int, int);
1108 void ContinueMoving(int, int);
1109 void Bang(int, int);
1110 void InitMovDir(int, int);
1111 void InitAmoebaNr(int, int);
1112 void NewHighScore(int, boolean);
1113
1114 void TestIfGoodThingHitsBadThing(int, int, int);
1115 void TestIfBadThingHitsGoodThing(int, int, int);
1116 void TestIfPlayerTouchesBadThing(int, int);
1117 void TestIfPlayerRunsIntoBadThing(int, int, int);
1118 void TestIfBadThingTouchesPlayer(int, int);
1119 void TestIfBadThingRunsIntoPlayer(int, int, int);
1120 void TestIfFriendTouchesBadThing(int, int);
1121 void TestIfBadThingTouchesFriend(int, int);
1122 void TestIfBadThingTouchesOtherBadThing(int, int);
1123 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1124
1125 void KillPlayer(struct PlayerInfo *);
1126 void BuryPlayer(struct PlayerInfo *);
1127 void RemovePlayer(struct PlayerInfo *);
1128 void ExitPlayer(struct PlayerInfo *);
1129
1130 static int getInvisibleActiveFromInvisibleElement(int);
1131 static int getInvisibleFromInvisibleActiveElement(int);
1132
1133 static void TestFieldAfterSnapping(int, int, int, int, int);
1134
1135 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1136
1137 // for detection of endless loops, caused by custom element programming
1138 // (using maximal playfield width x 10 is just a rough approximation)
1139 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1140
1141 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1142 {                                                                       \
1143   if (recursion_loop_detected)                                          \
1144     return (rc);                                                        \
1145                                                                         \
1146   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1147   {                                                                     \
1148     recursion_loop_detected = TRUE;                                     \
1149     recursion_loop_element = (e);                                       \
1150   }                                                                     \
1151                                                                         \
1152   recursion_loop_depth++;                                               \
1153 }
1154
1155 #define RECURSION_LOOP_DETECTION_END()                                  \
1156 {                                                                       \
1157   recursion_loop_depth--;                                               \
1158 }
1159
1160 static int recursion_loop_depth;
1161 static boolean recursion_loop_detected;
1162 static boolean recursion_loop_element;
1163
1164 static int map_player_action[MAX_PLAYERS];
1165
1166
1167 // ----------------------------------------------------------------------------
1168 // definition of elements that automatically change to other elements after
1169 // a specified time, eventually calling a function when changing
1170 // ----------------------------------------------------------------------------
1171
1172 // forward declaration for changer functions
1173 static void InitBuggyBase(int, int);
1174 static void WarnBuggyBase(int, int);
1175
1176 static void InitTrap(int, int);
1177 static void ActivateTrap(int, int);
1178 static void ChangeActiveTrap(int, int);
1179
1180 static void InitRobotWheel(int, int);
1181 static void RunRobotWheel(int, int);
1182 static void StopRobotWheel(int, int);
1183
1184 static void InitTimegateWheel(int, int);
1185 static void RunTimegateWheel(int, int);
1186
1187 static void InitMagicBallDelay(int, int);
1188 static void ActivateMagicBall(int, int);
1189
1190 struct ChangingElementInfo
1191 {
1192   int element;
1193   int target_element;
1194   int change_delay;
1195   void (*pre_change_function)(int x, int y);
1196   void (*change_function)(int x, int y);
1197   void (*post_change_function)(int x, int y);
1198 };
1199
1200 static struct ChangingElementInfo change_delay_list[] =
1201 {
1202   {
1203     EL_NUT_BREAKING,
1204     EL_EMERALD,
1205     6,
1206     NULL,
1207     NULL,
1208     NULL
1209   },
1210   {
1211     EL_PEARL_BREAKING,
1212     EL_EMPTY,
1213     8,
1214     NULL,
1215     NULL,
1216     NULL
1217   },
1218   {
1219     EL_EXIT_OPENING,
1220     EL_EXIT_OPEN,
1221     29,
1222     NULL,
1223     NULL,
1224     NULL
1225   },
1226   {
1227     EL_EXIT_CLOSING,
1228     EL_EXIT_CLOSED,
1229     29,
1230     NULL,
1231     NULL,
1232     NULL
1233   },
1234   {
1235     EL_STEEL_EXIT_OPENING,
1236     EL_STEEL_EXIT_OPEN,
1237     29,
1238     NULL,
1239     NULL,
1240     NULL
1241   },
1242   {
1243     EL_STEEL_EXIT_CLOSING,
1244     EL_STEEL_EXIT_CLOSED,
1245     29,
1246     NULL,
1247     NULL,
1248     NULL
1249   },
1250   {
1251     EL_EM_EXIT_OPENING,
1252     EL_EM_EXIT_OPEN,
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258   {
1259     EL_EM_EXIT_CLOSING,
1260     EL_EMPTY,
1261     29,
1262     NULL,
1263     NULL,
1264     NULL
1265   },
1266   {
1267     EL_EM_STEEL_EXIT_OPENING,
1268     EL_EM_STEEL_EXIT_OPEN,
1269     29,
1270     NULL,
1271     NULL,
1272     NULL
1273   },
1274   {
1275     EL_EM_STEEL_EXIT_CLOSING,
1276     EL_STEELWALL,
1277     29,
1278     NULL,
1279     NULL,
1280     NULL
1281   },
1282   {
1283     EL_SP_EXIT_OPENING,
1284     EL_SP_EXIT_OPEN,
1285     29,
1286     NULL,
1287     NULL,
1288     NULL
1289   },
1290   {
1291     EL_SP_EXIT_CLOSING,
1292     EL_SP_EXIT_CLOSED,
1293     29,
1294     NULL,
1295     NULL,
1296     NULL
1297   },
1298   {
1299     EL_SWITCHGATE_OPENING,
1300     EL_SWITCHGATE_OPEN,
1301     29,
1302     NULL,
1303     NULL,
1304     NULL
1305   },
1306   {
1307     EL_SWITCHGATE_CLOSING,
1308     EL_SWITCHGATE_CLOSED,
1309     29,
1310     NULL,
1311     NULL,
1312     NULL
1313   },
1314   {
1315     EL_TIMEGATE_OPENING,
1316     EL_TIMEGATE_OPEN,
1317     29,
1318     NULL,
1319     NULL,
1320     NULL
1321   },
1322   {
1323     EL_TIMEGATE_CLOSING,
1324     EL_TIMEGATE_CLOSED,
1325     29,
1326     NULL,
1327     NULL,
1328     NULL
1329   },
1330
1331   {
1332     EL_ACID_SPLASH_LEFT,
1333     EL_EMPTY,
1334     8,
1335     NULL,
1336     NULL,
1337     NULL
1338   },
1339   {
1340     EL_ACID_SPLASH_RIGHT,
1341     EL_EMPTY,
1342     8,
1343     NULL,
1344     NULL,
1345     NULL
1346   },
1347   {
1348     EL_SP_BUGGY_BASE,
1349     EL_SP_BUGGY_BASE_ACTIVATING,
1350     0,
1351     InitBuggyBase,
1352     NULL,
1353     NULL
1354   },
1355   {
1356     EL_SP_BUGGY_BASE_ACTIVATING,
1357     EL_SP_BUGGY_BASE_ACTIVE,
1358     0,
1359     InitBuggyBase,
1360     NULL,
1361     NULL
1362   },
1363   {
1364     EL_SP_BUGGY_BASE_ACTIVE,
1365     EL_SP_BUGGY_BASE,
1366     0,
1367     InitBuggyBase,
1368     WarnBuggyBase,
1369     NULL
1370   },
1371   {
1372     EL_TRAP,
1373     EL_TRAP_ACTIVE,
1374     0,
1375     InitTrap,
1376     NULL,
1377     ActivateTrap
1378   },
1379   {
1380     EL_TRAP_ACTIVE,
1381     EL_TRAP,
1382     31,
1383     NULL,
1384     ChangeActiveTrap,
1385     NULL
1386   },
1387   {
1388     EL_ROBOT_WHEEL_ACTIVE,
1389     EL_ROBOT_WHEEL,
1390     0,
1391     InitRobotWheel,
1392     RunRobotWheel,
1393     StopRobotWheel
1394   },
1395   {
1396     EL_TIMEGATE_SWITCH_ACTIVE,
1397     EL_TIMEGATE_SWITCH,
1398     0,
1399     InitTimegateWheel,
1400     RunTimegateWheel,
1401     NULL
1402   },
1403   {
1404     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1405     EL_DC_TIMEGATE_SWITCH,
1406     0,
1407     InitTimegateWheel,
1408     RunTimegateWheel,
1409     NULL
1410   },
1411   {
1412     EL_EMC_MAGIC_BALL_ACTIVE,
1413     EL_EMC_MAGIC_BALL_ACTIVE,
1414     0,
1415     InitMagicBallDelay,
1416     NULL,
1417     ActivateMagicBall
1418   },
1419   {
1420     EL_EMC_SPRING_BUMPER_ACTIVE,
1421     EL_EMC_SPRING_BUMPER,
1422     8,
1423     NULL,
1424     NULL,
1425     NULL
1426   },
1427   {
1428     EL_DIAGONAL_SHRINKING,
1429     EL_UNDEFINED,
1430     0,
1431     NULL,
1432     NULL,
1433     NULL
1434   },
1435   {
1436     EL_DIAGONAL_GROWING,
1437     EL_UNDEFINED,
1438     0,
1439     NULL,
1440     NULL,
1441     NULL,
1442   },
1443
1444   {
1445     EL_UNDEFINED,
1446     EL_UNDEFINED,
1447     -1,
1448     NULL,
1449     NULL,
1450     NULL
1451   }
1452 };
1453
1454 struct
1455 {
1456   int element;
1457   int push_delay_fixed, push_delay_random;
1458 }
1459 push_delay_list[] =
1460 {
1461   { EL_SPRING,                  0, 0 },
1462   { EL_BALLOON,                 0, 0 },
1463
1464   { EL_SOKOBAN_OBJECT,          2, 0 },
1465   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1466   { EL_SATELLITE,               2, 0 },
1467   { EL_SP_DISK_YELLOW,          2, 0 },
1468
1469   { EL_UNDEFINED,               0, 0 },
1470 };
1471
1472 struct
1473 {
1474   int element;
1475   int move_stepsize;
1476 }
1477 move_stepsize_list[] =
1478 {
1479   { EL_AMOEBA_DROP,             2 },
1480   { EL_AMOEBA_DROPPING,         2 },
1481   { EL_QUICKSAND_FILLING,       1 },
1482   { EL_QUICKSAND_EMPTYING,      1 },
1483   { EL_QUICKSAND_FAST_FILLING,  2 },
1484   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1485   { EL_MAGIC_WALL_FILLING,      2 },
1486   { EL_MAGIC_WALL_EMPTYING,     2 },
1487   { EL_BD_MAGIC_WALL_FILLING,   2 },
1488   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1489   { EL_DC_MAGIC_WALL_FILLING,   2 },
1490   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1491
1492   { EL_UNDEFINED,               0 },
1493 };
1494
1495 struct
1496 {
1497   int element;
1498   int count;
1499 }
1500 collect_count_list[] =
1501 {
1502   { EL_EMERALD,                 1 },
1503   { EL_BD_DIAMOND,              1 },
1504   { EL_EMERALD_YELLOW,          1 },
1505   { EL_EMERALD_RED,             1 },
1506   { EL_EMERALD_PURPLE,          1 },
1507   { EL_DIAMOND,                 3 },
1508   { EL_SP_INFOTRON,             1 },
1509   { EL_PEARL,                   5 },
1510   { EL_CRYSTAL,                 8 },
1511
1512   { EL_UNDEFINED,               0 },
1513 };
1514
1515 struct
1516 {
1517   int element;
1518   int direction;
1519 }
1520 access_direction_list[] =
1521 {
1522   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1523   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1524   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1525   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1526   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1527   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1528   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1529   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1530   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1531   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1532   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1533
1534   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1535   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1536   { EL_SP_PORT_UP,                                                   MV_DOWN },
1537   { EL_SP_PORT_DOWN,                                         MV_UP           },
1538   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1539   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1540   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1541   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1542   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1543   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1544   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1545   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1546   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1547   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1548   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1549   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1550   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1551   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1552   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1553
1554   { EL_UNDEFINED,                       MV_NONE                              }
1555 };
1556
1557 static struct XY xy_topdown[] =
1558 {
1559   {  0, -1 },
1560   { -1,  0 },
1561   { +1,  0 },
1562   {  0, +1 }
1563 };
1564
1565 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1566
1567 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1568 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1569 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1570                                  IS_JUST_CHANGING(x, y))
1571
1572 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1573
1574 // static variables for playfield scan mode (scanning forward or backward)
1575 static int playfield_scan_start_x = 0;
1576 static int playfield_scan_start_y = 0;
1577 static int playfield_scan_delta_x = 1;
1578 static int playfield_scan_delta_y = 1;
1579
1580 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1581                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1582                                      (y) += playfield_scan_delta_y)     \
1583                                 for ((x) = playfield_scan_start_x;      \
1584                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1585                                      (x) += playfield_scan_delta_x)
1586
1587 #ifdef DEBUG
1588 void DEBUG_SetMaximumDynamite(void)
1589 {
1590   int i;
1591
1592   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1593     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1594       local_player->inventory_element[local_player->inventory_size++] =
1595         EL_DYNAMITE;
1596 }
1597 #endif
1598
1599 static void InitPlayfieldScanModeVars(void)
1600 {
1601   if (game.use_reverse_scan_direction)
1602   {
1603     playfield_scan_start_x = lev_fieldx - 1;
1604     playfield_scan_start_y = lev_fieldy - 1;
1605
1606     playfield_scan_delta_x = -1;
1607     playfield_scan_delta_y = -1;
1608   }
1609   else
1610   {
1611     playfield_scan_start_x = 0;
1612     playfield_scan_start_y = 0;
1613
1614     playfield_scan_delta_x = 1;
1615     playfield_scan_delta_y = 1;
1616   }
1617 }
1618
1619 static void InitPlayfieldScanMode(int mode)
1620 {
1621   game.use_reverse_scan_direction =
1622     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1623
1624   InitPlayfieldScanModeVars();
1625 }
1626
1627 static int get_move_delay_from_stepsize(int move_stepsize)
1628 {
1629   move_stepsize =
1630     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1631
1632   // make sure that stepsize value is always a power of 2
1633   move_stepsize = (1 << log_2(move_stepsize));
1634
1635   return TILEX / move_stepsize;
1636 }
1637
1638 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1639                                boolean init_game)
1640 {
1641   int player_nr = player->index_nr;
1642   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1643   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1644
1645   // do no immediately change move delay -- the player might just be moving
1646   player->move_delay_value_next = move_delay;
1647
1648   // information if player can move must be set separately
1649   player->cannot_move = cannot_move;
1650
1651   if (init_game)
1652   {
1653     player->move_delay       = game.initial_move_delay[player_nr];
1654     player->move_delay_value = game.initial_move_delay_value[player_nr];
1655
1656     player->move_delay_value_next = -1;
1657
1658     player->move_delay_reset_counter = 0;
1659   }
1660 }
1661
1662 void GetPlayerConfig(void)
1663 {
1664   GameFrameDelay = setup.game_frame_delay;
1665
1666   if (!audio.sound_available)
1667     setup.sound_simple = FALSE;
1668
1669   if (!audio.loops_available)
1670     setup.sound_loops = FALSE;
1671
1672   if (!audio.music_available)
1673     setup.sound_music = FALSE;
1674
1675   if (!video.fullscreen_available)
1676     setup.fullscreen = FALSE;
1677
1678   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1679
1680   SetAudioMode(setup.sound);
1681 }
1682
1683 int GetElementFromGroupElement(int element)
1684 {
1685   if (IS_GROUP_ELEMENT(element))
1686   {
1687     struct ElementGroupInfo *group = element_info[element].group;
1688     int last_anim_random_frame = gfx.anim_random_frame;
1689     int element_pos;
1690
1691     if (group->choice_mode == ANIM_RANDOM)
1692       gfx.anim_random_frame = RND(group->num_elements_resolved);
1693
1694     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1695                                     group->choice_mode, 0,
1696                                     group->choice_pos);
1697
1698     if (group->choice_mode == ANIM_RANDOM)
1699       gfx.anim_random_frame = last_anim_random_frame;
1700
1701     group->choice_pos++;
1702
1703     element = group->element_resolved[element_pos];
1704   }
1705
1706   return element;
1707 }
1708
1709 static void IncrementSokobanFieldsNeeded(void)
1710 {
1711   if (level.sb_fields_needed)
1712     game.sokoban_fields_still_needed++;
1713 }
1714
1715 static void IncrementSokobanObjectsNeeded(void)
1716 {
1717   if (level.sb_objects_needed)
1718     game.sokoban_objects_still_needed++;
1719 }
1720
1721 static void DecrementSokobanFieldsNeeded(void)
1722 {
1723   if (game.sokoban_fields_still_needed > 0)
1724     game.sokoban_fields_still_needed--;
1725 }
1726
1727 static void DecrementSokobanObjectsNeeded(void)
1728 {
1729   if (game.sokoban_objects_still_needed > 0)
1730     game.sokoban_objects_still_needed--;
1731 }
1732
1733 static void InitPlayerField(int x, int y, int element, boolean init_game)
1734 {
1735   if (element == EL_SP_MURPHY)
1736   {
1737     if (init_game)
1738     {
1739       if (stored_player[0].present)
1740       {
1741         Tile[x][y] = EL_SP_MURPHY_CLONE;
1742
1743         return;
1744       }
1745       else
1746       {
1747         stored_player[0].initial_element = element;
1748         stored_player[0].use_murphy = TRUE;
1749
1750         if (!level.use_artwork_element[0])
1751           stored_player[0].artwork_element = EL_SP_MURPHY;
1752       }
1753
1754       Tile[x][y] = EL_PLAYER_1;
1755     }
1756   }
1757
1758   if (init_game)
1759   {
1760     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1761     int jx = player->jx, jy = player->jy;
1762
1763     player->present = TRUE;
1764
1765     player->block_last_field = (element == EL_SP_MURPHY ?
1766                                 level.sp_block_last_field :
1767                                 level.block_last_field);
1768
1769     // ---------- initialize player's last field block delay ------------------
1770
1771     // always start with reliable default value (no adjustment needed)
1772     player->block_delay_adjustment = 0;
1773
1774     // special case 1: in Supaplex, Murphy blocks last field one more frame
1775     if (player->block_last_field && element == EL_SP_MURPHY)
1776       player->block_delay_adjustment = 1;
1777
1778     // special case 2: in game engines before 3.1.1, blocking was different
1779     if (game.use_block_last_field_bug)
1780       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1781
1782     if (!network.enabled || player->connected_network)
1783     {
1784       player->active = TRUE;
1785
1786       // remove potentially duplicate players
1787       if (IN_LEV_FIELD(jx, jy) && StorePlayer[jx][jy] == Tile[x][y])
1788         StorePlayer[jx][jy] = 0;
1789
1790       StorePlayer[x][y] = Tile[x][y];
1791
1792 #if DEBUG_INIT_PLAYER
1793       Debug("game:init:player", "- player element %d activated",
1794             player->element_nr);
1795       Debug("game:init:player", "  (local player is %d and currently %s)",
1796             local_player->element_nr,
1797             local_player->active ? "active" : "not active");
1798     }
1799 #endif
1800
1801     Tile[x][y] = EL_EMPTY;
1802
1803     player->jx = player->last_jx = x;
1804     player->jy = player->last_jy = y;
1805   }
1806
1807   // always check if player was just killed and should be reanimated
1808   {
1809     int player_nr = GET_PLAYER_NR(element);
1810     struct PlayerInfo *player = &stored_player[player_nr];
1811
1812     if (player->active && player->killed)
1813       player->reanimated = TRUE; // if player was just killed, reanimate him
1814   }
1815 }
1816
1817 static void InitField(int x, int y, boolean init_game)
1818 {
1819   int element = Tile[x][y];
1820
1821   switch (element)
1822   {
1823     case EL_SP_MURPHY:
1824     case EL_PLAYER_1:
1825     case EL_PLAYER_2:
1826     case EL_PLAYER_3:
1827     case EL_PLAYER_4:
1828       InitPlayerField(x, y, element, init_game);
1829       break;
1830
1831     case EL_SOKOBAN_FIELD_PLAYER:
1832       element = Tile[x][y] = EL_PLAYER_1;
1833       InitField(x, y, init_game);
1834
1835       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1836       InitField(x, y, init_game);
1837       break;
1838
1839     case EL_SOKOBAN_FIELD_EMPTY:
1840       IncrementSokobanFieldsNeeded();
1841       break;
1842
1843     case EL_SOKOBAN_OBJECT:
1844       IncrementSokobanObjectsNeeded();
1845       break;
1846
1847     case EL_STONEBLOCK:
1848       if (x < lev_fieldx - 1 && Tile[x + 1][y] == EL_ACID)
1849         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1850       else if (x > 0 && Tile[x - 1][y] == EL_ACID)
1851         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1852       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPLEFT)
1853         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1854       else if (y > 0 && Tile[x][y - 1] == EL_ACID)
1855         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1856       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPRIGHT)
1857         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1858       break;
1859
1860     case EL_BUG:
1861     case EL_BUG_RIGHT:
1862     case EL_BUG_UP:
1863     case EL_BUG_LEFT:
1864     case EL_BUG_DOWN:
1865     case EL_SPACESHIP:
1866     case EL_SPACESHIP_RIGHT:
1867     case EL_SPACESHIP_UP:
1868     case EL_SPACESHIP_LEFT:
1869     case EL_SPACESHIP_DOWN:
1870     case EL_BD_BUTTERFLY:
1871     case EL_BD_BUTTERFLY_RIGHT:
1872     case EL_BD_BUTTERFLY_UP:
1873     case EL_BD_BUTTERFLY_LEFT:
1874     case EL_BD_BUTTERFLY_DOWN:
1875     case EL_BD_FIREFLY:
1876     case EL_BD_FIREFLY_RIGHT:
1877     case EL_BD_FIREFLY_UP:
1878     case EL_BD_FIREFLY_LEFT:
1879     case EL_BD_FIREFLY_DOWN:
1880     case EL_PACMAN_RIGHT:
1881     case EL_PACMAN_UP:
1882     case EL_PACMAN_LEFT:
1883     case EL_PACMAN_DOWN:
1884     case EL_YAMYAM:
1885     case EL_YAMYAM_LEFT:
1886     case EL_YAMYAM_RIGHT:
1887     case EL_YAMYAM_UP:
1888     case EL_YAMYAM_DOWN:
1889     case EL_DARK_YAMYAM:
1890     case EL_ROBOT:
1891     case EL_PACMAN:
1892     case EL_SP_SNIKSNAK:
1893     case EL_SP_ELECTRON:
1894     case EL_MOLE:
1895     case EL_MOLE_LEFT:
1896     case EL_MOLE_RIGHT:
1897     case EL_MOLE_UP:
1898     case EL_MOLE_DOWN:
1899     case EL_SPRING_LEFT:
1900     case EL_SPRING_RIGHT:
1901       InitMovDir(x, y);
1902       break;
1903
1904     case EL_AMOEBA_FULL:
1905     case EL_BD_AMOEBA:
1906       InitAmoebaNr(x, y);
1907       break;
1908
1909     case EL_AMOEBA_DROP:
1910       if (y == lev_fieldy - 1)
1911       {
1912         Tile[x][y] = EL_AMOEBA_GROWING;
1913         Store[x][y] = EL_AMOEBA_WET;
1914       }
1915       break;
1916
1917     case EL_DYNAMITE_ACTIVE:
1918     case EL_SP_DISK_RED_ACTIVE:
1919     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1920     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1921     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1922     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1923       MovDelay[x][y] = 96;
1924       break;
1925
1926     case EL_EM_DYNAMITE_ACTIVE:
1927       MovDelay[x][y] = 32;
1928       break;
1929
1930     case EL_LAMP:
1931       game.lights_still_needed++;
1932       break;
1933
1934     case EL_PENGUIN:
1935       game.friends_still_needed++;
1936       break;
1937
1938     case EL_PIG:
1939     case EL_DRAGON:
1940       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1941       break;
1942
1943     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1944     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1945     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1946     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1947     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1948     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1949     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1950     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1951     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1952     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1953     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1954     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1955       if (init_game)
1956       {
1957         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1958         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1959         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1960
1961         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1962         {
1963           game.belt_dir[belt_nr] = belt_dir;
1964           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1965         }
1966         else    // more than one switch -- set it like the first switch
1967         {
1968           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1969         }
1970       }
1971       break;
1972
1973     case EL_LIGHT_SWITCH_ACTIVE:
1974       if (init_game)
1975         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1976       break;
1977
1978     case EL_INVISIBLE_STEELWALL:
1979     case EL_INVISIBLE_WALL:
1980     case EL_INVISIBLE_SAND:
1981       if (game.light_time_left > 0 ||
1982           game.lenses_time_left > 0)
1983         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1984       break;
1985
1986     case EL_EMC_MAGIC_BALL:
1987       if (game.ball_active)
1988         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1989       break;
1990
1991     case EL_EMC_MAGIC_BALL_SWITCH:
1992       if (game.ball_active)
1993         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1994       break;
1995
1996     case EL_TRIGGER_PLAYER:
1997     case EL_TRIGGER_ELEMENT:
1998     case EL_TRIGGER_CE_VALUE:
1999     case EL_TRIGGER_CE_SCORE:
2000     case EL_SELF:
2001     case EL_ANY_ELEMENT:
2002     case EL_CURRENT_CE_VALUE:
2003     case EL_CURRENT_CE_SCORE:
2004     case EL_PREV_CE_1:
2005     case EL_PREV_CE_2:
2006     case EL_PREV_CE_3:
2007     case EL_PREV_CE_4:
2008     case EL_PREV_CE_5:
2009     case EL_PREV_CE_6:
2010     case EL_PREV_CE_7:
2011     case EL_PREV_CE_8:
2012     case EL_NEXT_CE_1:
2013     case EL_NEXT_CE_2:
2014     case EL_NEXT_CE_3:
2015     case EL_NEXT_CE_4:
2016     case EL_NEXT_CE_5:
2017     case EL_NEXT_CE_6:
2018     case EL_NEXT_CE_7:
2019     case EL_NEXT_CE_8:
2020       // reference elements should not be used on the playfield
2021       Tile[x][y] = EL_EMPTY;
2022       break;
2023
2024     default:
2025       if (IS_CUSTOM_ELEMENT(element))
2026       {
2027         if (CAN_MOVE(element))
2028           InitMovDir(x, y);
2029
2030         if (!element_info[element].use_last_ce_value || init_game)
2031           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2032       }
2033       else if (IS_GROUP_ELEMENT(element))
2034       {
2035         Tile[x][y] = GetElementFromGroupElement(element);
2036
2037         InitField(x, y, init_game);
2038       }
2039       else if (IS_EMPTY_ELEMENT(element))
2040       {
2041         GfxElementEmpty[x][y] = element;
2042         Tile[x][y] = EL_EMPTY;
2043
2044         if (element_info[element].use_gfx_element)
2045           game.use_masked_elements = TRUE;
2046       }
2047
2048       break;
2049   }
2050
2051   if (!init_game)
2052     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2053 }
2054
2055 static void InitField_WithBug1(int x, int y, boolean init_game)
2056 {
2057   InitField(x, y, init_game);
2058
2059   // not needed to call InitMovDir() -- already done by InitField()!
2060   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2061       CAN_MOVE(Tile[x][y]))
2062     InitMovDir(x, y);
2063 }
2064
2065 static void InitField_WithBug2(int x, int y, boolean init_game)
2066 {
2067   int old_element = Tile[x][y];
2068
2069   InitField(x, y, init_game);
2070
2071   // not needed to call InitMovDir() -- already done by InitField()!
2072   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2073       CAN_MOVE(old_element) &&
2074       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2075     InitMovDir(x, y);
2076
2077   /* this case is in fact a combination of not less than three bugs:
2078      first, it calls InitMovDir() for elements that can move, although this is
2079      already done by InitField(); then, it checks the element that was at this
2080      field _before_ the call to InitField() (which can change it); lastly, it
2081      was not called for "mole with direction" elements, which were treated as
2082      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2083   */
2084 }
2085
2086 static int get_key_element_from_nr(int key_nr)
2087 {
2088   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2089                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2090                           EL_EM_KEY_1 : EL_KEY_1);
2091
2092   return key_base_element + key_nr;
2093 }
2094
2095 static int get_next_dropped_element(struct PlayerInfo *player)
2096 {
2097   return (player->inventory_size > 0 ?
2098           player->inventory_element[player->inventory_size - 1] :
2099           player->inventory_infinite_element != EL_UNDEFINED ?
2100           player->inventory_infinite_element :
2101           player->dynabombs_left > 0 ?
2102           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2103           EL_UNDEFINED);
2104 }
2105
2106 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2107 {
2108   // pos >= 0: get element from bottom of the stack;
2109   // pos <  0: get element from top of the stack
2110
2111   if (pos < 0)
2112   {
2113     int min_inventory_size = -pos;
2114     int inventory_pos = player->inventory_size - min_inventory_size;
2115     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2116
2117     return (player->inventory_size >= min_inventory_size ?
2118             player->inventory_element[inventory_pos] :
2119             player->inventory_infinite_element != EL_UNDEFINED ?
2120             player->inventory_infinite_element :
2121             player->dynabombs_left >= min_dynabombs_left ?
2122             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2123             EL_UNDEFINED);
2124   }
2125   else
2126   {
2127     int min_dynabombs_left = pos + 1;
2128     int min_inventory_size = pos + 1 - player->dynabombs_left;
2129     int inventory_pos = pos - player->dynabombs_left;
2130
2131     return (player->inventory_infinite_element != EL_UNDEFINED ?
2132             player->inventory_infinite_element :
2133             player->dynabombs_left >= min_dynabombs_left ?
2134             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2135             player->inventory_size >= min_inventory_size ?
2136             player->inventory_element[inventory_pos] :
2137             EL_UNDEFINED);
2138   }
2139 }
2140
2141 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2142 {
2143   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2144   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2145   int compare_result;
2146
2147   if (gpo1->sort_priority != gpo2->sort_priority)
2148     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2149   else
2150     compare_result = gpo1->nr - gpo2->nr;
2151
2152   return compare_result;
2153 }
2154
2155 int getPlayerInventorySize(int player_nr)
2156 {
2157   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2158     return game_em.ply[player_nr]->dynamite;
2159   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2160     return game_sp.red_disk_count;
2161   else
2162     return stored_player[player_nr].inventory_size;
2163 }
2164
2165 static void InitGameControlValues(void)
2166 {
2167   int i;
2168
2169   for (i = 0; game_panel_controls[i].nr != -1; i++)
2170   {
2171     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2172     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2173     struct TextPosInfo *pos = gpc->pos;
2174     int nr = gpc->nr;
2175     int type = gpc->type;
2176
2177     if (nr != i)
2178     {
2179       Error("'game_panel_controls' structure corrupted at %d", i);
2180
2181       Fail("this should not happen -- please debug");
2182     }
2183
2184     // force update of game controls after initialization
2185     gpc->value = gpc->last_value = -1;
2186     gpc->frame = gpc->last_frame = -1;
2187     gpc->gfx_frame = -1;
2188
2189     // determine panel value width for later calculation of alignment
2190     if (type == TYPE_INTEGER || type == TYPE_STRING)
2191     {
2192       pos->width = pos->size * getFontWidth(pos->font);
2193       pos->height = getFontHeight(pos->font);
2194     }
2195     else if (type == TYPE_ELEMENT)
2196     {
2197       pos->width = pos->size;
2198       pos->height = pos->size;
2199     }
2200
2201     // fill structure for game panel draw order
2202     gpo->nr = gpc->nr;
2203     gpo->sort_priority = pos->sort_priority;
2204   }
2205
2206   // sort game panel controls according to sort_priority and control number
2207   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2208         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2209 }
2210
2211 static void UpdatePlayfieldElementCount(void)
2212 {
2213   boolean use_element_count = FALSE;
2214   int i, j, x, y;
2215
2216   // first check if it is needed at all to calculate playfield element count
2217   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2218     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2219       use_element_count = TRUE;
2220
2221   if (!use_element_count)
2222     return;
2223
2224   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2225     element_info[i].element_count = 0;
2226
2227   SCAN_PLAYFIELD(x, y)
2228   {
2229     element_info[Tile[x][y]].element_count++;
2230   }
2231
2232   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2233     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2234       if (IS_IN_GROUP(j, i))
2235         element_info[EL_GROUP_START + i].element_count +=
2236           element_info[j].element_count;
2237 }
2238
2239 static void UpdateGameControlValues(void)
2240 {
2241   int i, k;
2242   int time = (game.LevelSolved ?
2243               game.LevelSolved_CountingTime :
2244               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2245               game_em.lev->time :
2246               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2247               game_sp.time_played :
2248               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2249               game_mm.energy_left :
2250               game.no_level_time_limit ? TimePlayed : TimeLeft);
2251   int score = (game.LevelSolved ?
2252                game.LevelSolved_CountingScore :
2253                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2254                game_em.lev->score :
2255                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2256                game_sp.score :
2257                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2258                game_mm.score :
2259                game.score);
2260   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2261               game_em.lev->gems_needed :
2262               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2263               game_sp.infotrons_still_needed :
2264               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2265               game_mm.kettles_still_needed :
2266               game.gems_still_needed);
2267   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2268                      game_em.lev->gems_needed > 0 :
2269                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2270                      game_sp.infotrons_still_needed > 0 :
2271                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2272                      game_mm.kettles_still_needed > 0 ||
2273                      game_mm.lights_still_needed > 0 :
2274                      game.gems_still_needed > 0 ||
2275                      game.sokoban_fields_still_needed > 0 ||
2276                      game.sokoban_objects_still_needed > 0 ||
2277                      game.lights_still_needed > 0);
2278   int health = (game.LevelSolved ?
2279                 game.LevelSolved_CountingHealth :
2280                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2281                 MM_HEALTH(game_mm.laser_overload_value) :
2282                 game.health);
2283   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2284
2285   UpdatePlayfieldElementCount();
2286
2287   // update game panel control values
2288
2289   // used instead of "level_nr" (for network games)
2290   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2291   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2292
2293   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2294   for (i = 0; i < MAX_NUM_KEYS; i++)
2295     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2296   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2297   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2298
2299   if (game.centered_player_nr == -1)
2300   {
2301     for (i = 0; i < MAX_PLAYERS; i++)
2302     {
2303       // only one player in Supaplex game engine
2304       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2305         break;
2306
2307       for (k = 0; k < MAX_NUM_KEYS; k++)
2308       {
2309         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2310         {
2311           if (game_em.ply[i]->keys & (1 << k))
2312             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2313               get_key_element_from_nr(k);
2314         }
2315         else if (stored_player[i].key[k])
2316           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2317             get_key_element_from_nr(k);
2318       }
2319
2320       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2321         getPlayerInventorySize(i);
2322
2323       if (stored_player[i].num_white_keys > 0)
2324         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2325           EL_DC_KEY_WHITE;
2326
2327       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2328         stored_player[i].num_white_keys;
2329     }
2330   }
2331   else
2332   {
2333     int player_nr = game.centered_player_nr;
2334
2335     for (k = 0; k < MAX_NUM_KEYS; k++)
2336     {
2337       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2338       {
2339         if (game_em.ply[player_nr]->keys & (1 << k))
2340           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2341             get_key_element_from_nr(k);
2342       }
2343       else if (stored_player[player_nr].key[k])
2344         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2345           get_key_element_from_nr(k);
2346     }
2347
2348     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2349       getPlayerInventorySize(player_nr);
2350
2351     if (stored_player[player_nr].num_white_keys > 0)
2352       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2353
2354     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2355       stored_player[player_nr].num_white_keys;
2356   }
2357
2358   // re-arrange keys on game panel, if needed or if defined by style settings
2359   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2360   {
2361     int nr = GAME_PANEL_KEY_1 + i;
2362     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2363     struct TextPosInfo *pos = gpc->pos;
2364
2365     // skip check if key is not in the player's inventory
2366     if (gpc->value == EL_EMPTY)
2367       continue;
2368
2369     // check if keys should be arranged on panel from left to right
2370     if (pos->style == STYLE_LEFTMOST_POSITION)
2371     {
2372       // check previous key positions (left from current key)
2373       for (k = 0; k < i; k++)
2374       {
2375         int nr_new = GAME_PANEL_KEY_1 + k;
2376
2377         if (game_panel_controls[nr_new].value == EL_EMPTY)
2378         {
2379           game_panel_controls[nr_new].value = gpc->value;
2380           gpc->value = EL_EMPTY;
2381
2382           break;
2383         }
2384       }
2385     }
2386
2387     // check if "undefined" keys can be placed at some other position
2388     if (pos->x == -1 && pos->y == -1)
2389     {
2390       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2391
2392       // 1st try: display key at the same position as normal or EM keys
2393       if (game_panel_controls[nr_new].value == EL_EMPTY)
2394       {
2395         game_panel_controls[nr_new].value = gpc->value;
2396       }
2397       else
2398       {
2399         // 2nd try: display key at the next free position in the key panel
2400         for (k = 0; k < STD_NUM_KEYS; k++)
2401         {
2402           nr_new = GAME_PANEL_KEY_1 + k;
2403
2404           if (game_panel_controls[nr_new].value == EL_EMPTY)
2405           {
2406             game_panel_controls[nr_new].value = gpc->value;
2407
2408             break;
2409           }
2410         }
2411       }
2412     }
2413   }
2414
2415   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2416   {
2417     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2418       get_inventory_element_from_pos(local_player, i);
2419     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2420       get_inventory_element_from_pos(local_player, -i - 1);
2421   }
2422
2423   game_panel_controls[GAME_PANEL_SCORE].value = score;
2424   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2425
2426   game_panel_controls[GAME_PANEL_TIME].value = time;
2427
2428   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2429   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2430   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2431
2432   if (level.time == 0)
2433     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2434   else
2435     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2436
2437   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2438   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2439
2440   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2441
2442   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2443     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2444      EL_EMPTY);
2445   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2446     local_player->shield_normal_time_left;
2447   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2448     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2449      EL_EMPTY);
2450   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2451     local_player->shield_deadly_time_left;
2452
2453   game_panel_controls[GAME_PANEL_EXIT].value =
2454     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2455
2456   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2457     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2458   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2459     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2460      EL_EMC_MAGIC_BALL_SWITCH);
2461
2462   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2463     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2464   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2465     game.light_time_left;
2466
2467   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2468     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2469   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2470     game.timegate_time_left;
2471
2472   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2473     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2474
2475   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2476     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2477   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2478     game.lenses_time_left;
2479
2480   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2481     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2482   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2483     game.magnify_time_left;
2484
2485   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2486     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2487      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2488      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2489      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2490      EL_BALLOON_SWITCH_NONE);
2491
2492   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2493     local_player->dynabomb_count;
2494   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2495     local_player->dynabomb_size;
2496   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2497     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2498
2499   game_panel_controls[GAME_PANEL_PENGUINS].value =
2500     game.friends_still_needed;
2501
2502   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2503     game.sokoban_objects_still_needed;
2504   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2505     game.sokoban_fields_still_needed;
2506
2507   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2508     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2509
2510   for (i = 0; i < NUM_BELTS; i++)
2511   {
2512     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2513       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2514        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2515     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2516       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2517   }
2518
2519   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2520     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2521   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2522     game.magic_wall_time_left;
2523
2524   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2525     local_player->gravity;
2526
2527   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2528     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2529
2530   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2531     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2532       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2533        game.panel.element[i].id : EL_UNDEFINED);
2534
2535   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2536     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2537       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2538        element_info[game.panel.element_count[i].id].element_count : 0);
2539
2540   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2541     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2542       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2543        element_info[game.panel.ce_score[i].id].collect_score : 0);
2544
2545   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2546     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2547       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2548        element_info[game.panel.ce_score_element[i].id].collect_score :
2549        EL_UNDEFINED);
2550
2551   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2552   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2553   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2554
2555   // update game panel control frames
2556
2557   for (i = 0; game_panel_controls[i].nr != -1; i++)
2558   {
2559     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2560
2561     if (gpc->type == TYPE_ELEMENT)
2562     {
2563       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2564       {
2565         int last_anim_random_frame = gfx.anim_random_frame;
2566         int element = gpc->value;
2567         int graphic = el2panelimg(element);
2568         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2569                                sync_random_frame :
2570                                graphic_info[graphic].anim_global_anim_sync ?
2571                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2572
2573         if (gpc->value != gpc->last_value)
2574         {
2575           gpc->gfx_frame = 0;
2576           gpc->gfx_random = init_gfx_random;
2577         }
2578         else
2579         {
2580           gpc->gfx_frame++;
2581
2582           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2583               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2584             gpc->gfx_random = init_gfx_random;
2585         }
2586
2587         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2588           gfx.anim_random_frame = gpc->gfx_random;
2589
2590         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2591           gpc->gfx_frame = element_info[element].collect_score;
2592
2593         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2594
2595         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2596           gfx.anim_random_frame = last_anim_random_frame;
2597       }
2598     }
2599     else if (gpc->type == TYPE_GRAPHIC)
2600     {
2601       if (gpc->graphic != IMG_UNDEFINED)
2602       {
2603         int last_anim_random_frame = gfx.anim_random_frame;
2604         int graphic = gpc->graphic;
2605         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2606                                sync_random_frame :
2607                                graphic_info[graphic].anim_global_anim_sync ?
2608                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2609
2610         if (gpc->value != gpc->last_value)
2611         {
2612           gpc->gfx_frame = 0;
2613           gpc->gfx_random = init_gfx_random;
2614         }
2615         else
2616         {
2617           gpc->gfx_frame++;
2618
2619           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2620               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2621             gpc->gfx_random = init_gfx_random;
2622         }
2623
2624         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2625           gfx.anim_random_frame = gpc->gfx_random;
2626
2627         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2628
2629         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2630           gfx.anim_random_frame = last_anim_random_frame;
2631       }
2632     }
2633   }
2634 }
2635
2636 static void DisplayGameControlValues(void)
2637 {
2638   boolean redraw_panel = FALSE;
2639   int i;
2640
2641   for (i = 0; game_panel_controls[i].nr != -1; i++)
2642   {
2643     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2644
2645     if (PANEL_DEACTIVATED(gpc->pos))
2646       continue;
2647
2648     if (gpc->value == gpc->last_value &&
2649         gpc->frame == gpc->last_frame)
2650       continue;
2651
2652     redraw_panel = TRUE;
2653   }
2654
2655   if (!redraw_panel)
2656     return;
2657
2658   // copy default game door content to main double buffer
2659
2660   // !!! CHECK AGAIN !!!
2661   SetPanelBackground();
2662   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2663   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2664
2665   // redraw game control buttons
2666   RedrawGameButtons();
2667
2668   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2669
2670   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2671   {
2672     int nr = game_panel_order[i].nr;
2673     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2674     struct TextPosInfo *pos = gpc->pos;
2675     int type = gpc->type;
2676     int value = gpc->value;
2677     int frame = gpc->frame;
2678     int size = pos->size;
2679     int font = pos->font;
2680     boolean draw_masked = pos->draw_masked;
2681     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2682
2683     if (PANEL_DEACTIVATED(pos))
2684       continue;
2685
2686     if (pos->class == get_hash_from_key("extra_panel_items") &&
2687         !setup.prefer_extra_panel_items)
2688       continue;
2689
2690     gpc->last_value = value;
2691     gpc->last_frame = frame;
2692
2693     if (type == TYPE_INTEGER)
2694     {
2695       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2696           nr == GAME_PANEL_INVENTORY_COUNT ||
2697           nr == GAME_PANEL_SCORE ||
2698           nr == GAME_PANEL_HIGHSCORE ||
2699           nr == GAME_PANEL_TIME)
2700       {
2701         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2702
2703         if (use_dynamic_size)           // use dynamic number of digits
2704         {
2705           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 :
2706                               nr == GAME_PANEL_INVENTORY_COUNT ||
2707                               nr == GAME_PANEL_TIME ? 1000 : 100000);
2708           int size_add = (nr == GAME_PANEL_LEVEL_NUMBER ||
2709                           nr == GAME_PANEL_INVENTORY_COUNT ||
2710                           nr == GAME_PANEL_TIME ? 1 : 2);
2711           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 :
2712                        nr == GAME_PANEL_INVENTORY_COUNT ||
2713                        nr == GAME_PANEL_TIME ? 3 : 5);
2714           int size2 = size1 + size_add;
2715           int font1 = pos->font;
2716           int font2 = pos->font_alt;
2717
2718           size = (value < value_change ? size1 : size2);
2719           font = (value < value_change ? font1 : font2);
2720         }
2721       }
2722
2723       // correct text size if "digits" is zero or less
2724       if (size <= 0)
2725         size = strlen(int2str(value, size));
2726
2727       // dynamically correct text alignment
2728       pos->width = size * getFontWidth(font);
2729
2730       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2731                   int2str(value, size), font, mask_mode);
2732     }
2733     else if (type == TYPE_ELEMENT)
2734     {
2735       int element, graphic;
2736       Bitmap *src_bitmap;
2737       int src_x, src_y;
2738       int width, height;
2739       int dst_x = PANEL_XPOS(pos);
2740       int dst_y = PANEL_YPOS(pos);
2741
2742       if (value != EL_UNDEFINED && value != EL_EMPTY)
2743       {
2744         element = value;
2745         graphic = el2panelimg(value);
2746
2747 #if 0
2748         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2749               element, EL_NAME(element), size);
2750 #endif
2751
2752         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2753           size = TILESIZE;
2754
2755         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2756                               &src_x, &src_y);
2757
2758         width  = graphic_info[graphic].width  * size / TILESIZE;
2759         height = graphic_info[graphic].height * size / TILESIZE;
2760
2761         if (draw_masked)
2762           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2763                            dst_x, dst_y);
2764         else
2765           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2766                      dst_x, dst_y);
2767       }
2768     }
2769     else if (type == TYPE_GRAPHIC)
2770     {
2771       int graphic        = gpc->graphic;
2772       int graphic_active = gpc->graphic_active;
2773       Bitmap *src_bitmap;
2774       int src_x, src_y;
2775       int width, height;
2776       int dst_x = PANEL_XPOS(pos);
2777       int dst_y = PANEL_YPOS(pos);
2778       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2779                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2780
2781       if (graphic != IMG_UNDEFINED && !skip)
2782       {
2783         if (pos->style == STYLE_REVERSE)
2784           value = 100 - value;
2785
2786         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2787
2788         if (pos->direction & MV_HORIZONTAL)
2789         {
2790           width  = graphic_info[graphic_active].width * value / 100;
2791           height = graphic_info[graphic_active].height;
2792
2793           if (pos->direction == MV_LEFT)
2794           {
2795             src_x += graphic_info[graphic_active].width - width;
2796             dst_x += graphic_info[graphic_active].width - width;
2797           }
2798         }
2799         else
2800         {
2801           width  = graphic_info[graphic_active].width;
2802           height = graphic_info[graphic_active].height * value / 100;
2803
2804           if (pos->direction == MV_UP)
2805           {
2806             src_y += graphic_info[graphic_active].height - height;
2807             dst_y += graphic_info[graphic_active].height - height;
2808           }
2809         }
2810
2811         if (draw_masked)
2812           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2813                            dst_x, dst_y);
2814         else
2815           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2816                      dst_x, dst_y);
2817
2818         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2819
2820         if (pos->direction & MV_HORIZONTAL)
2821         {
2822           if (pos->direction == MV_RIGHT)
2823           {
2824             src_x += width;
2825             dst_x += width;
2826           }
2827           else
2828           {
2829             dst_x = PANEL_XPOS(pos);
2830           }
2831
2832           width = graphic_info[graphic].width - width;
2833         }
2834         else
2835         {
2836           if (pos->direction == MV_DOWN)
2837           {
2838             src_y += height;
2839             dst_y += height;
2840           }
2841           else
2842           {
2843             dst_y = PANEL_YPOS(pos);
2844           }
2845
2846           height = graphic_info[graphic].height - height;
2847         }
2848
2849         if (draw_masked)
2850           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2851                            dst_x, dst_y);
2852         else
2853           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2854                      dst_x, dst_y);
2855       }
2856     }
2857     else if (type == TYPE_STRING)
2858     {
2859       boolean active = (value != 0);
2860       char *state_normal = "off";
2861       char *state_active = "on";
2862       char *state = (active ? state_active : state_normal);
2863       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2864                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2865                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2866                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2867
2868       if (nr == GAME_PANEL_GRAVITY_STATE)
2869       {
2870         int font1 = pos->font;          // (used for normal state)
2871         int font2 = pos->font_alt;      // (used for active state)
2872
2873         font = (active ? font2 : font1);
2874       }
2875
2876       if (s != NULL)
2877       {
2878         char *s_cut;
2879
2880         if (size <= 0)
2881         {
2882           // don't truncate output if "chars" is zero or less
2883           size = strlen(s);
2884
2885           // dynamically correct text alignment
2886           pos->width = size * getFontWidth(font);
2887         }
2888
2889         s_cut = getStringCopyN(s, size);
2890
2891         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2892                     s_cut, font, mask_mode);
2893
2894         free(s_cut);
2895       }
2896     }
2897
2898     redraw_mask |= REDRAW_DOOR_1;
2899   }
2900
2901   SetGameStatus(GAME_MODE_PLAYING);
2902 }
2903
2904 void UpdateAndDisplayGameControlValues(void)
2905 {
2906   if (tape.deactivate_display)
2907     return;
2908
2909   UpdateGameControlValues();
2910   DisplayGameControlValues();
2911 }
2912
2913 void UpdateGameDoorValues(void)
2914 {
2915   UpdateGameControlValues();
2916 }
2917
2918 void DrawGameDoorValues(void)
2919 {
2920   DisplayGameControlValues();
2921 }
2922
2923
2924 // ============================================================================
2925 // InitGameEngine()
2926 // ----------------------------------------------------------------------------
2927 // initialize game engine due to level / tape version number
2928 // ============================================================================
2929
2930 static void InitGameEngine(void)
2931 {
2932   int i, j, k, l, x, y;
2933
2934   // set game engine from tape file when re-playing, else from level file
2935   game.engine_version = (tape.playing ? tape.engine_version :
2936                          level.game_version);
2937
2938   // set single or multi-player game mode (needed for re-playing tapes)
2939   game.team_mode = setup.team_mode;
2940
2941   if (tape.playing)
2942   {
2943     int num_players = 0;
2944
2945     for (i = 0; i < MAX_PLAYERS; i++)
2946       if (tape.player_participates[i])
2947         num_players++;
2948
2949     // multi-player tapes contain input data for more than one player
2950     game.team_mode = (num_players > 1);
2951   }
2952
2953 #if 0
2954   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2955         level.game_version);
2956   Debug("game:init:level", "          tape.file_version   == %06d",
2957         tape.file_version);
2958   Debug("game:init:level", "          tape.game_version   == %06d",
2959         tape.game_version);
2960   Debug("game:init:level", "          tape.engine_version == %06d",
2961         tape.engine_version);
2962   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2963         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2964 #endif
2965
2966   // --------------------------------------------------------------------------
2967   // set flags for bugs and changes according to active game engine version
2968   // --------------------------------------------------------------------------
2969
2970   /*
2971     Summary of bugfix:
2972     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2973
2974     Bug was introduced in version:
2975     2.0.1
2976
2977     Bug was fixed in version:
2978     4.2.0.0
2979
2980     Description:
2981     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2982     but the property "can fall" was missing, which caused some levels to be
2983     unsolvable. This was fixed in version 4.2.0.0.
2984
2985     Affected levels/tapes:
2986     An example for a tape that was fixed by this bugfix is tape 029 from the
2987     level set "rnd_sam_bateman".
2988     The wrong behaviour will still be used for all levels or tapes that were
2989     created/recorded with it. An example for this is tape 023 from the level
2990     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2991   */
2992
2993   boolean use_amoeba_dropping_cannot_fall_bug =
2994     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2995       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2996      (tape.playing &&
2997       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2998       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2999
3000   /*
3001     Summary of bugfix/change:
3002     Fixed move speed of elements entering or leaving magic wall.
3003
3004     Fixed/changed in version:
3005     2.0.1
3006
3007     Description:
3008     Before 2.0.1, move speed of elements entering or leaving magic wall was
3009     twice as fast as it is now.
3010     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
3011
3012     Affected levels/tapes:
3013     The first condition is generally needed for all levels/tapes before version
3014     2.0.1, which might use the old behaviour before it was changed; known tapes
3015     that are affected: Tape 014 from the level set "rnd_conor_mancone".
3016     The second condition is an exception from the above case and is needed for
3017     the special case of tapes recorded with game (not engine!) version 2.0.1 or
3018     above, but before it was known that this change would break tapes like the
3019     above and was fixed in 4.2.0.0, so that the changed behaviour was active
3020     although the engine version while recording maybe was before 2.0.1. There
3021     are a lot of tapes that are affected by this exception, like tape 006 from
3022     the level set "rnd_conor_mancone".
3023   */
3024
3025   boolean use_old_move_stepsize_for_magic_wall =
3026     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
3027      !(tape.playing &&
3028        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3029        tape.game_version <  VERSION_IDENT(4,2,0,0)));
3030
3031   /*
3032     Summary of bugfix/change:
3033     Fixed handling for custom elements that change when pushed by the player.
3034
3035     Fixed/changed in version:
3036     3.1.0
3037
3038     Description:
3039     Before 3.1.0, custom elements that "change when pushing" changed directly
3040     after the player started pushing them (until then handled in "DigField()").
3041     Since 3.1.0, these custom elements are not changed until the "pushing"
3042     move of the element is finished (now handled in "ContinueMoving()").
3043
3044     Affected levels/tapes:
3045     The first condition is generally needed for all levels/tapes before version
3046     3.1.0, which might use the old behaviour before it was changed; known tapes
3047     that are affected are some tapes from the level set "Walpurgis Gardens" by
3048     Jamie Cullen.
3049     The second condition is an exception from the above case and is needed for
3050     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3051     above (including some development versions of 3.1.0), but before it was
3052     known that this change would break tapes like the above and was fixed in
3053     3.1.1, so that the changed behaviour was active although the engine version
3054     while recording maybe was before 3.1.0. There is at least one tape that is
3055     affected by this exception, which is the tape for the one-level set "Bug
3056     Machine" by Juergen Bonhagen.
3057   */
3058
3059   game.use_change_when_pushing_bug =
3060     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3061      !(tape.playing &&
3062        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3063        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3064
3065   /*
3066     Summary of bugfix/change:
3067     Fixed handling for blocking the field the player leaves when moving.
3068
3069     Fixed/changed in version:
3070     3.1.1
3071
3072     Description:
3073     Before 3.1.1, when "block last field when moving" was enabled, the field
3074     the player is leaving when moving was blocked for the time of the move,
3075     and was directly unblocked afterwards. This resulted in the last field
3076     being blocked for exactly one less than the number of frames of one player
3077     move. Additionally, even when blocking was disabled, the last field was
3078     blocked for exactly one frame.
3079     Since 3.1.1, due to changes in player movement handling, the last field
3080     is not blocked at all when blocking is disabled. When blocking is enabled,
3081     the last field is blocked for exactly the number of frames of one player
3082     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3083     last field is blocked for exactly one more than the number of frames of
3084     one player move.
3085
3086     Affected levels/tapes:
3087     (!!! yet to be determined -- probably many !!!)
3088   */
3089
3090   game.use_block_last_field_bug =
3091     (game.engine_version < VERSION_IDENT(3,1,1,0));
3092
3093   /* various special flags and settings for native Emerald Mine game engine */
3094
3095   game_em.use_single_button =
3096     (game.engine_version > VERSION_IDENT(4,0,0,2));
3097
3098   game_em.use_snap_key_bug =
3099     (game.engine_version < VERSION_IDENT(4,0,1,0));
3100
3101   game_em.use_random_bug =
3102     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3103
3104   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3105
3106   game_em.use_old_explosions            = use_old_em_engine;
3107   game_em.use_old_android               = use_old_em_engine;
3108   game_em.use_old_push_elements         = use_old_em_engine;
3109   game_em.use_old_push_into_acid        = use_old_em_engine;
3110
3111   game_em.use_wrap_around               = !use_old_em_engine;
3112
3113   // --------------------------------------------------------------------------
3114
3115   // set maximal allowed number of custom element changes per game frame
3116   game.max_num_changes_per_frame = 1;
3117
3118   // default scan direction: scan playfield from top/left to bottom/right
3119   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3120
3121   // dynamically adjust element properties according to game engine version
3122   InitElementPropertiesEngine(game.engine_version);
3123
3124   // ---------- initialize special element properties -------------------------
3125
3126   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3127   if (use_amoeba_dropping_cannot_fall_bug)
3128     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3129
3130   // ---------- initialize player's initial move delay ------------------------
3131
3132   // dynamically adjust player properties according to level information
3133   for (i = 0; i < MAX_PLAYERS; i++)
3134     game.initial_move_delay_value[i] =
3135       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3136
3137   // dynamically adjust player properties according to game engine version
3138   for (i = 0; i < MAX_PLAYERS; i++)
3139     game.initial_move_delay[i] =
3140       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3141        game.initial_move_delay_value[i] : 0);
3142
3143   // ---------- initialize player's initial push delay ------------------------
3144
3145   // dynamically adjust player properties according to game engine version
3146   game.initial_push_delay_value =
3147     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3148
3149   // ---------- initialize changing elements ----------------------------------
3150
3151   // initialize changing elements information
3152   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3153   {
3154     struct ElementInfo *ei = &element_info[i];
3155
3156     // this pointer might have been changed in the level editor
3157     ei->change = &ei->change_page[0];
3158
3159     if (!IS_CUSTOM_ELEMENT(i))
3160     {
3161       ei->change->target_element = EL_EMPTY_SPACE;
3162       ei->change->delay_fixed = 0;
3163       ei->change->delay_random = 0;
3164       ei->change->delay_frames = 1;
3165     }
3166
3167     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3168     {
3169       ei->has_change_event[j] = FALSE;
3170
3171       ei->event_page_nr[j] = 0;
3172       ei->event_page[j] = &ei->change_page[0];
3173     }
3174   }
3175
3176   // add changing elements from pre-defined list
3177   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3178   {
3179     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3180     struct ElementInfo *ei = &element_info[ch_delay->element];
3181
3182     ei->change->target_element       = ch_delay->target_element;
3183     ei->change->delay_fixed          = ch_delay->change_delay;
3184
3185     ei->change->pre_change_function  = ch_delay->pre_change_function;
3186     ei->change->change_function      = ch_delay->change_function;
3187     ei->change->post_change_function = ch_delay->post_change_function;
3188
3189     ei->change->can_change = TRUE;
3190     ei->change->can_change_or_has_action = TRUE;
3191
3192     ei->has_change_event[CE_DELAY] = TRUE;
3193
3194     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3195     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3196   }
3197
3198   // ---------- initialize internal run-time variables ------------------------
3199
3200   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3201   {
3202     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3203
3204     for (j = 0; j < ei->num_change_pages; j++)
3205     {
3206       ei->change_page[j].can_change_or_has_action =
3207         (ei->change_page[j].can_change |
3208          ei->change_page[j].has_action);
3209     }
3210   }
3211
3212   // add change events from custom element configuration
3213   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3214   {
3215     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3216
3217     for (j = 0; j < ei->num_change_pages; j++)
3218     {
3219       if (!ei->change_page[j].can_change_or_has_action)
3220         continue;
3221
3222       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3223       {
3224         // only add event page for the first page found with this event
3225         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3226         {
3227           ei->has_change_event[k] = TRUE;
3228
3229           ei->event_page_nr[k] = j;
3230           ei->event_page[k] = &ei->change_page[j];
3231         }
3232       }
3233     }
3234   }
3235
3236   // ---------- initialize reference elements in change conditions ------------
3237
3238   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3239   {
3240     int element = EL_CUSTOM_START + i;
3241     struct ElementInfo *ei = &element_info[element];
3242
3243     for (j = 0; j < ei->num_change_pages; j++)
3244     {
3245       int trigger_element = ei->change_page[j].initial_trigger_element;
3246
3247       if (trigger_element >= EL_PREV_CE_8 &&
3248           trigger_element <= EL_NEXT_CE_8)
3249         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3250
3251       ei->change_page[j].trigger_element = trigger_element;
3252     }
3253   }
3254
3255   // ---------- initialize run-time trigger player and element ----------------
3256
3257   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3258   {
3259     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3260
3261     for (j = 0; j < ei->num_change_pages; j++)
3262     {
3263       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3264       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3265       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3266       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3267       ei->change_page[j].actual_trigger_ce_value = 0;
3268       ei->change_page[j].actual_trigger_ce_score = 0;
3269     }
3270   }
3271
3272   // ---------- initialize trigger events -------------------------------------
3273
3274   // initialize trigger events information
3275   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3276     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3277       trigger_events[i][j] = FALSE;
3278
3279   // add trigger events from element change event properties
3280   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3281   {
3282     struct ElementInfo *ei = &element_info[i];
3283
3284     for (j = 0; j < ei->num_change_pages; j++)
3285     {
3286       if (!ei->change_page[j].can_change_or_has_action)
3287         continue;
3288
3289       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3290       {
3291         int trigger_element = ei->change_page[j].trigger_element;
3292
3293         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3294         {
3295           if (ei->change_page[j].has_event[k])
3296           {
3297             if (IS_GROUP_ELEMENT(trigger_element))
3298             {
3299               struct ElementGroupInfo *group =
3300                 element_info[trigger_element].group;
3301
3302               for (l = 0; l < group->num_elements_resolved; l++)
3303                 trigger_events[group->element_resolved[l]][k] = TRUE;
3304             }
3305             else if (trigger_element == EL_ANY_ELEMENT)
3306               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3307                 trigger_events[l][k] = TRUE;
3308             else
3309               trigger_events[trigger_element][k] = TRUE;
3310           }
3311         }
3312       }
3313     }
3314   }
3315
3316   // ---------- initialize push delay -----------------------------------------
3317
3318   // initialize push delay values to default
3319   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3320   {
3321     if (!IS_CUSTOM_ELEMENT(i))
3322     {
3323       // set default push delay values (corrected since version 3.0.7-1)
3324       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3325       {
3326         element_info[i].push_delay_fixed = 2;
3327         element_info[i].push_delay_random = 8;
3328       }
3329       else
3330       {
3331         element_info[i].push_delay_fixed = 8;
3332         element_info[i].push_delay_random = 8;
3333       }
3334     }
3335   }
3336
3337   // set push delay value for certain elements from pre-defined list
3338   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3339   {
3340     int e = push_delay_list[i].element;
3341
3342     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3343     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3344   }
3345
3346   // set push delay value for Supaplex elements for newer engine versions
3347   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3348   {
3349     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3350     {
3351       if (IS_SP_ELEMENT(i))
3352       {
3353         // set SP push delay to just enough to push under a falling zonk
3354         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3355
3356         element_info[i].push_delay_fixed  = delay;
3357         element_info[i].push_delay_random = 0;
3358       }
3359     }
3360   }
3361
3362   // ---------- initialize move stepsize --------------------------------------
3363
3364   // initialize move stepsize values to default
3365   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3366     if (!IS_CUSTOM_ELEMENT(i))
3367       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3368
3369   // set move stepsize value for certain elements from pre-defined list
3370   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3371   {
3372     int e = move_stepsize_list[i].element;
3373
3374     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3375
3376     // set move stepsize value for certain elements for older engine versions
3377     if (use_old_move_stepsize_for_magic_wall)
3378     {
3379       if (e == EL_MAGIC_WALL_FILLING ||
3380           e == EL_MAGIC_WALL_EMPTYING ||
3381           e == EL_BD_MAGIC_WALL_FILLING ||
3382           e == EL_BD_MAGIC_WALL_EMPTYING)
3383         element_info[e].move_stepsize *= 2;
3384     }
3385   }
3386
3387   // ---------- initialize collect score --------------------------------------
3388
3389   // initialize collect score values for custom elements from initial value
3390   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3391     if (IS_CUSTOM_ELEMENT(i))
3392       element_info[i].collect_score = element_info[i].collect_score_initial;
3393
3394   // ---------- initialize collect count --------------------------------------
3395
3396   // initialize collect count values for non-custom elements
3397   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3398     if (!IS_CUSTOM_ELEMENT(i))
3399       element_info[i].collect_count_initial = 0;
3400
3401   // add collect count values for all elements from pre-defined list
3402   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3403     element_info[collect_count_list[i].element].collect_count_initial =
3404       collect_count_list[i].count;
3405
3406   // ---------- initialize access direction -----------------------------------
3407
3408   // initialize access direction values to default (access from every side)
3409   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3410     if (!IS_CUSTOM_ELEMENT(i))
3411       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3412
3413   // set access direction value for certain elements from pre-defined list
3414   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3415     element_info[access_direction_list[i].element].access_direction =
3416       access_direction_list[i].direction;
3417
3418   // ---------- initialize explosion content ----------------------------------
3419   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3420   {
3421     if (IS_CUSTOM_ELEMENT(i))
3422       continue;
3423
3424     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3425     {
3426       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3427
3428       element_info[i].content.e[x][y] =
3429         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3430          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3431          i == EL_PLAYER_3 ? EL_EMERALD :
3432          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3433          i == EL_MOLE ? EL_EMERALD_RED :
3434          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3435          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3436          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3437          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3438          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3439          i == EL_WALL_EMERALD ? EL_EMERALD :
3440          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3441          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3442          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3443          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3444          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3445          i == EL_WALL_PEARL ? EL_PEARL :
3446          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3447          EL_EMPTY);
3448     }
3449   }
3450
3451   // ---------- initialize recursion detection --------------------------------
3452   recursion_loop_depth = 0;
3453   recursion_loop_detected = FALSE;
3454   recursion_loop_element = EL_UNDEFINED;
3455
3456   // ---------- initialize graphics engine ------------------------------------
3457   game.scroll_delay_value =
3458     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3459      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3460      !setup.forced_scroll_delay           ? 0 :
3461      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3462   game.scroll_delay_value =
3463     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3464
3465   // ---------- initialize game engine snapshots ------------------------------
3466   for (i = 0; i < MAX_PLAYERS; i++)
3467     game.snapshot.last_action[i] = 0;
3468   game.snapshot.changed_action = FALSE;
3469   game.snapshot.collected_item = FALSE;
3470   game.snapshot.mode =
3471     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3472      SNAPSHOT_MODE_EVERY_STEP :
3473      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3474      SNAPSHOT_MODE_EVERY_MOVE :
3475      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3476      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3477   game.snapshot.save_snapshot = FALSE;
3478
3479   // ---------- initialize level time for Supaplex engine ---------------------
3480   // Supaplex levels with time limit currently unsupported -- should be added
3481   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3482     level.time = 0;
3483
3484   // ---------- initialize flags for handling game actions --------------------
3485
3486   // set flags for game actions to default values
3487   game.use_key_actions = TRUE;
3488   game.use_mouse_actions = FALSE;
3489
3490   // when using Mirror Magic game engine, handle mouse events only
3491   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3492   {
3493     game.use_key_actions = FALSE;
3494     game.use_mouse_actions = TRUE;
3495   }
3496
3497   // check for custom elements with mouse click events
3498   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3499   {
3500     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3501     {
3502       int element = EL_CUSTOM_START + i;
3503
3504       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3505           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3506           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3507           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3508         game.use_mouse_actions = TRUE;
3509     }
3510   }
3511 }
3512
3513 static int get_num_special_action(int element, int action_first,
3514                                   int action_last)
3515 {
3516   int num_special_action = 0;
3517   int i, j;
3518
3519   for (i = action_first; i <= action_last; i++)
3520   {
3521     boolean found = FALSE;
3522
3523     for (j = 0; j < NUM_DIRECTIONS; j++)
3524       if (el_act_dir2img(element, i, j) !=
3525           el_act_dir2img(element, ACTION_DEFAULT, j))
3526         found = TRUE;
3527
3528     if (found)
3529       num_special_action++;
3530     else
3531       break;
3532   }
3533
3534   return num_special_action;
3535 }
3536
3537
3538 // ============================================================================
3539 // InitGame()
3540 // ----------------------------------------------------------------------------
3541 // initialize and start new game
3542 // ============================================================================
3543
3544 #if DEBUG_INIT_PLAYER
3545 static void DebugPrintPlayerStatus(char *message)
3546 {
3547   int i;
3548
3549   if (!options.debug)
3550     return;
3551
3552   Debug("game:init:player", "%s:", message);
3553
3554   for (i = 0; i < MAX_PLAYERS; i++)
3555   {
3556     struct PlayerInfo *player = &stored_player[i];
3557
3558     Debug("game:init:player",
3559           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3560           i + 1,
3561           player->present,
3562           player->connected,
3563           player->connected_locally,
3564           player->connected_network,
3565           player->active,
3566           (local_player == player ? " (local player)" : ""));
3567   }
3568 }
3569 #endif
3570
3571 void InitGame(void)
3572 {
3573   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3574   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3575   int fade_mask = REDRAW_FIELD;
3576   boolean restarting = (game_status == GAME_MODE_PLAYING);
3577   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3578   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3579   int initial_move_dir = MV_DOWN;
3580   int i, j, x, y;
3581
3582   // required here to update video display before fading (FIX THIS)
3583   DrawMaskedBorder(REDRAW_DOOR_2);
3584
3585   if (!game.restart_level)
3586     CloseDoor(DOOR_CLOSE_1);
3587
3588   if (restarting)
3589   {
3590     // force fading out global animations displayed during game play
3591     SetGameStatus(GAME_MODE_PSEUDO_RESTARTING);
3592   }
3593   else
3594   {
3595     SetGameStatus(GAME_MODE_PLAYING);
3596   }
3597
3598   if (level_editor_test_game)
3599     FadeSkipNextFadeOut();
3600   else
3601     FadeSetEnterScreen();
3602
3603   if (CheckFadeAll())
3604     fade_mask = REDRAW_ALL;
3605
3606   FadeLevelSoundsAndMusic();
3607
3608   ExpireSoundLoops(TRUE);
3609
3610   FadeOut(fade_mask);
3611
3612   if (restarting)
3613   {
3614     // force restarting global animations displayed during game play
3615     RestartGlobalAnimsByStatus(GAME_MODE_PSEUDO_RESTARTING);
3616
3617     SetGameStatus(GAME_MODE_PLAYING);
3618   }
3619
3620   if (level_editor_test_game)
3621     FadeSkipNextFadeIn();
3622
3623   // needed if different viewport properties defined for playing
3624   ChangeViewportPropertiesIfNeeded();
3625
3626   ClearField();
3627
3628   DrawCompleteVideoDisplay();
3629
3630   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3631
3632   InitGameEngine();
3633   InitGameControlValues();
3634
3635   if (tape.recording)
3636   {
3637     // initialize tape actions from game when recording tape
3638     tape.use_key_actions   = game.use_key_actions;
3639     tape.use_mouse_actions = game.use_mouse_actions;
3640
3641     // initialize visible playfield size when recording tape (for team mode)
3642     tape.scr_fieldx = SCR_FIELDX;
3643     tape.scr_fieldy = SCR_FIELDY;
3644   }
3645
3646   // don't play tapes over network
3647   network_playing = (network.enabled && !tape.playing);
3648
3649   for (i = 0; i < MAX_PLAYERS; i++)
3650   {
3651     struct PlayerInfo *player = &stored_player[i];
3652
3653     player->index_nr = i;
3654     player->index_bit = (1 << i);
3655     player->element_nr = EL_PLAYER_1 + i;
3656
3657     player->present = FALSE;
3658     player->active = FALSE;
3659     player->mapped = FALSE;
3660
3661     player->killed = FALSE;
3662     player->reanimated = FALSE;
3663     player->buried = FALSE;
3664
3665     player->action = 0;
3666     player->effective_action = 0;
3667     player->programmed_action = 0;
3668     player->snap_action = 0;
3669
3670     player->mouse_action.lx = 0;
3671     player->mouse_action.ly = 0;
3672     player->mouse_action.button = 0;
3673     player->mouse_action.button_hint = 0;
3674
3675     player->effective_mouse_action.lx = 0;
3676     player->effective_mouse_action.ly = 0;
3677     player->effective_mouse_action.button = 0;
3678     player->effective_mouse_action.button_hint = 0;
3679
3680     for (j = 0; j < MAX_NUM_KEYS; j++)
3681       player->key[j] = FALSE;
3682
3683     player->num_white_keys = 0;
3684
3685     player->dynabomb_count = 0;
3686     player->dynabomb_size = 1;
3687     player->dynabombs_left = 0;
3688     player->dynabomb_xl = FALSE;
3689
3690     player->MovDir = initial_move_dir;
3691     player->MovPos = 0;
3692     player->GfxPos = 0;
3693     player->GfxDir = initial_move_dir;
3694     player->GfxAction = ACTION_DEFAULT;
3695     player->Frame = 0;
3696     player->StepFrame = 0;
3697
3698     player->initial_element = player->element_nr;
3699     player->artwork_element =
3700       (level.use_artwork_element[i] ? level.artwork_element[i] :
3701        player->element_nr);
3702     player->use_murphy = FALSE;
3703
3704     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3705     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3706
3707     player->gravity = level.initial_player_gravity[i];
3708
3709     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3710
3711     player->actual_frame_counter.count = 0;
3712     player->actual_frame_counter.value = 1;
3713
3714     player->step_counter = 0;
3715
3716     player->last_move_dir = initial_move_dir;
3717
3718     player->is_active = FALSE;
3719
3720     player->is_waiting = FALSE;
3721     player->is_moving = FALSE;
3722     player->is_auto_moving = FALSE;
3723     player->is_digging = FALSE;
3724     player->is_snapping = FALSE;
3725     player->is_collecting = FALSE;
3726     player->is_pushing = FALSE;
3727     player->is_switching = FALSE;
3728     player->is_dropping = FALSE;
3729     player->is_dropping_pressed = FALSE;
3730
3731     player->is_bored = FALSE;
3732     player->is_sleeping = FALSE;
3733
3734     player->was_waiting = TRUE;
3735     player->was_moving = FALSE;
3736     player->was_snapping = FALSE;
3737     player->was_dropping = FALSE;
3738
3739     player->force_dropping = FALSE;
3740
3741     player->frame_counter_bored = -1;
3742     player->frame_counter_sleeping = -1;
3743
3744     player->anim_delay_counter = 0;
3745     player->post_delay_counter = 0;
3746
3747     player->dir_waiting = initial_move_dir;
3748     player->action_waiting = ACTION_DEFAULT;
3749     player->last_action_waiting = ACTION_DEFAULT;
3750     player->special_action_bored = ACTION_DEFAULT;
3751     player->special_action_sleeping = ACTION_DEFAULT;
3752
3753     player->switch_x = -1;
3754     player->switch_y = -1;
3755
3756     player->drop_x = -1;
3757     player->drop_y = -1;
3758
3759     player->show_envelope = 0;
3760
3761     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3762
3763     player->push_delay       = -1;      // initialized when pushing starts
3764     player->push_delay_value = game.initial_push_delay_value;
3765
3766     player->drop_delay = 0;
3767     player->drop_pressed_delay = 0;
3768
3769     player->last_jx = -1;
3770     player->last_jy = -1;
3771     player->jx = -1;
3772     player->jy = -1;
3773
3774     player->shield_normal_time_left = 0;
3775     player->shield_deadly_time_left = 0;
3776
3777     player->last_removed_element = EL_UNDEFINED;
3778
3779     player->inventory_infinite_element = EL_UNDEFINED;
3780     player->inventory_size = 0;
3781
3782     if (level.use_initial_inventory[i])
3783     {
3784       for (j = 0; j < level.initial_inventory_size[i]; j++)
3785       {
3786         int element = level.initial_inventory_content[i][j];
3787         int collect_count = element_info[element].collect_count_initial;
3788         int k;
3789
3790         if (!IS_CUSTOM_ELEMENT(element))
3791           collect_count = 1;
3792
3793         if (collect_count == 0)
3794           player->inventory_infinite_element = element;
3795         else
3796           for (k = 0; k < collect_count; k++)
3797             if (player->inventory_size < MAX_INVENTORY_SIZE)
3798               player->inventory_element[player->inventory_size++] = element;
3799       }
3800     }
3801
3802     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3803     SnapField(player, 0, 0);
3804
3805     map_player_action[i] = i;
3806   }
3807
3808   network_player_action_received = FALSE;
3809
3810   // initial null action
3811   if (network_playing)
3812     SendToServer_MovePlayer(MV_NONE);
3813
3814   FrameCounter = 0;
3815   TimeFrames = 0;
3816   TimePlayed = 0;
3817   TimeLeft = level.time;
3818   TapeTime = 0;
3819
3820   ScreenMovDir = MV_NONE;
3821   ScreenMovPos = 0;
3822   ScreenGfxPos = 0;
3823
3824   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3825
3826   game.robot_wheel_x = -1;
3827   game.robot_wheel_y = -1;
3828
3829   game.exit_x = -1;
3830   game.exit_y = -1;
3831
3832   game.all_players_gone = FALSE;
3833
3834   game.LevelSolved = FALSE;
3835   game.GameOver = FALSE;
3836
3837   game.GamePlayed = !tape.playing;
3838
3839   game.LevelSolved_GameWon = FALSE;
3840   game.LevelSolved_GameEnd = FALSE;
3841   game.LevelSolved_SaveTape = FALSE;
3842   game.LevelSolved_SaveScore = FALSE;
3843
3844   game.LevelSolved_CountingTime = 0;
3845   game.LevelSolved_CountingScore = 0;
3846   game.LevelSolved_CountingHealth = 0;
3847
3848   game.panel.active = TRUE;
3849
3850   game.no_level_time_limit = (level.time == 0);
3851   game.time_limit = (leveldir_current->time_limit && setup.time_limit);
3852
3853   game.yamyam_content_nr = 0;
3854   game.robot_wheel_active = FALSE;
3855   game.magic_wall_active = FALSE;
3856   game.magic_wall_time_left = 0;
3857   game.light_time_left = 0;
3858   game.timegate_time_left = 0;
3859   game.switchgate_pos = 0;
3860   game.wind_direction = level.wind_direction_initial;
3861
3862   game.time_final = 0;
3863   game.score_time_final = 0;
3864
3865   game.score = 0;
3866   game.score_final = 0;
3867
3868   game.health = MAX_HEALTH;
3869   game.health_final = MAX_HEALTH;
3870
3871   game.gems_still_needed = level.gems_needed;
3872   game.sokoban_fields_still_needed = 0;
3873   game.sokoban_objects_still_needed = 0;
3874   game.lights_still_needed = 0;
3875   game.players_still_needed = 0;
3876   game.friends_still_needed = 0;
3877
3878   game.lenses_time_left = 0;
3879   game.magnify_time_left = 0;
3880
3881   game.ball_active = level.ball_active_initial;
3882   game.ball_content_nr = 0;
3883
3884   game.explosions_delayed = TRUE;
3885
3886   game.envelope_active = FALSE;
3887
3888   // special case: set custom artwork setting to initial value
3889   game.use_masked_elements = game.use_masked_elements_initial;
3890
3891   for (i = 0; i < NUM_BELTS; i++)
3892   {
3893     game.belt_dir[i] = MV_NONE;
3894     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3895   }
3896
3897   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3898     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3899
3900 #if DEBUG_INIT_PLAYER
3901   DebugPrintPlayerStatus("Player status at level initialization");
3902 #endif
3903
3904   SCAN_PLAYFIELD(x, y)
3905   {
3906     Tile[x][y] = Last[x][y] = level.field[x][y];
3907     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3908     ChangeDelay[x][y] = 0;
3909     ChangePage[x][y] = -1;
3910     CustomValue[x][y] = 0;              // initialized in InitField()
3911     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3912     AmoebaNr[x][y] = 0;
3913     WasJustMoving[x][y] = 0;
3914     WasJustFalling[x][y] = 0;
3915     CheckCollision[x][y] = 0;
3916     CheckImpact[x][y] = 0;
3917     Stop[x][y] = FALSE;
3918     Pushed[x][y] = FALSE;
3919
3920     ChangeCount[x][y] = 0;
3921     ChangeEvent[x][y] = -1;
3922
3923     ExplodePhase[x][y] = 0;
3924     ExplodeDelay[x][y] = 0;
3925     ExplodeField[x][y] = EX_TYPE_NONE;
3926
3927     RunnerVisit[x][y] = 0;
3928     PlayerVisit[x][y] = 0;
3929
3930     GfxFrame[x][y] = 0;
3931     GfxRandom[x][y] = INIT_GFX_RANDOM();
3932     GfxRandomStatic[x][y] = INIT_GFX_RANDOM();
3933     GfxElement[x][y] = EL_UNDEFINED;
3934     GfxElementEmpty[x][y] = EL_EMPTY;
3935     GfxAction[x][y] = ACTION_DEFAULT;
3936     GfxDir[x][y] = MV_NONE;
3937     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3938   }
3939
3940   SCAN_PLAYFIELD(x, y)
3941   {
3942     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3943       emulate_bd = FALSE;
3944     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3945       emulate_sp = FALSE;
3946
3947     InitField(x, y, TRUE);
3948
3949     ResetGfxAnimation(x, y);
3950   }
3951
3952   InitBeltMovement();
3953
3954   for (i = 0; i < MAX_PLAYERS; i++)
3955   {
3956     struct PlayerInfo *player = &stored_player[i];
3957
3958     // set number of special actions for bored and sleeping animation
3959     player->num_special_action_bored =
3960       get_num_special_action(player->artwork_element,
3961                              ACTION_BORING_1, ACTION_BORING_LAST);
3962     player->num_special_action_sleeping =
3963       get_num_special_action(player->artwork_element,
3964                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3965   }
3966
3967   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3968                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3969
3970   // initialize type of slippery elements
3971   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3972   {
3973     if (!IS_CUSTOM_ELEMENT(i))
3974     {
3975       // default: elements slip down either to the left or right randomly
3976       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3977
3978       // SP style elements prefer to slip down on the left side
3979       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3980         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3981
3982       // BD style elements prefer to slip down on the left side
3983       if (game.emulation == EMU_BOULDERDASH)
3984         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3985     }
3986   }
3987
3988   // initialize explosion and ignition delay
3989   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3990   {
3991     if (!IS_CUSTOM_ELEMENT(i))
3992     {
3993       int num_phase = 8;
3994       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3995                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3996                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3997       int last_phase = (num_phase + 1) * delay;
3998       int half_phase = (num_phase / 2) * delay;
3999
4000       element_info[i].explosion_delay = last_phase - 1;
4001       element_info[i].ignition_delay = half_phase;
4002
4003       if (i == EL_BLACK_ORB)
4004         element_info[i].ignition_delay = 1;
4005     }
4006   }
4007
4008   // correct non-moving belts to start moving left
4009   for (i = 0; i < NUM_BELTS; i++)
4010     if (game.belt_dir[i] == MV_NONE)
4011       game.belt_dir_nr[i] = 3;          // not moving, next moving left
4012
4013 #if USE_NEW_PLAYER_ASSIGNMENTS
4014   // use preferred player also in local single-player mode
4015   if (!network.enabled && !game.team_mode)
4016   {
4017     int new_index_nr = setup.network_player_nr;
4018
4019     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
4020     {
4021       for (i = 0; i < MAX_PLAYERS; i++)
4022         stored_player[i].connected_locally = FALSE;
4023
4024       stored_player[new_index_nr].connected_locally = TRUE;
4025     }
4026   }
4027
4028   for (i = 0; i < MAX_PLAYERS; i++)
4029   {
4030     stored_player[i].connected = FALSE;
4031
4032     // in network game mode, the local player might not be the first player
4033     if (stored_player[i].connected_locally)
4034       local_player = &stored_player[i];
4035   }
4036
4037   if (!network.enabled)
4038     local_player->connected = TRUE;
4039
4040   if (tape.playing)
4041   {
4042     for (i = 0; i < MAX_PLAYERS; i++)
4043       stored_player[i].connected = tape.player_participates[i];
4044   }
4045   else if (network.enabled)
4046   {
4047     // add team mode players connected over the network (needed for correct
4048     // assignment of player figures from level to locally playing players)
4049
4050     for (i = 0; i < MAX_PLAYERS; i++)
4051       if (stored_player[i].connected_network)
4052         stored_player[i].connected = TRUE;
4053   }
4054   else if (game.team_mode)
4055   {
4056     // try to guess locally connected team mode players (needed for correct
4057     // assignment of player figures from level to locally playing players)
4058
4059     for (i = 0; i < MAX_PLAYERS; i++)
4060       if (setup.input[i].use_joystick ||
4061           setup.input[i].key.left != KSYM_UNDEFINED)
4062         stored_player[i].connected = TRUE;
4063   }
4064
4065 #if DEBUG_INIT_PLAYER
4066   DebugPrintPlayerStatus("Player status after level initialization");
4067 #endif
4068
4069 #if DEBUG_INIT_PLAYER
4070   Debug("game:init:player", "Reassigning players ...");
4071 #endif
4072
4073   // check if any connected player was not found in playfield
4074   for (i = 0; i < MAX_PLAYERS; i++)
4075   {
4076     struct PlayerInfo *player = &stored_player[i];
4077
4078     if (player->connected && !player->present)
4079     {
4080       struct PlayerInfo *field_player = NULL;
4081
4082 #if DEBUG_INIT_PLAYER
4083       Debug("game:init:player",
4084             "- looking for field player for player %d ...", i + 1);
4085 #endif
4086
4087       // assign first free player found that is present in the playfield
4088
4089       // first try: look for unmapped playfield player that is not connected
4090       for (j = 0; j < MAX_PLAYERS; j++)
4091         if (field_player == NULL &&
4092             stored_player[j].present &&
4093             !stored_player[j].mapped &&
4094             !stored_player[j].connected)
4095           field_player = &stored_player[j];
4096
4097       // second try: look for *any* unmapped playfield player
4098       for (j = 0; j < MAX_PLAYERS; j++)
4099         if (field_player == NULL &&
4100             stored_player[j].present &&
4101             !stored_player[j].mapped)
4102           field_player = &stored_player[j];
4103
4104       if (field_player != NULL)
4105       {
4106         int jx = field_player->jx, jy = field_player->jy;
4107
4108 #if DEBUG_INIT_PLAYER
4109         Debug("game:init:player", "- found player %d",
4110               field_player->index_nr + 1);
4111 #endif
4112
4113         player->present = FALSE;
4114         player->active = FALSE;
4115
4116         field_player->present = TRUE;
4117         field_player->active = TRUE;
4118
4119         /*
4120         player->initial_element = field_player->initial_element;
4121         player->artwork_element = field_player->artwork_element;
4122
4123         player->block_last_field       = field_player->block_last_field;
4124         player->block_delay_adjustment = field_player->block_delay_adjustment;
4125         */
4126
4127         StorePlayer[jx][jy] = field_player->element_nr;
4128
4129         field_player->jx = field_player->last_jx = jx;
4130         field_player->jy = field_player->last_jy = jy;
4131
4132         if (local_player == player)
4133           local_player = field_player;
4134
4135         map_player_action[field_player->index_nr] = i;
4136
4137         field_player->mapped = TRUE;
4138
4139 #if DEBUG_INIT_PLAYER
4140         Debug("game:init:player", "- map_player_action[%d] == %d",
4141               field_player->index_nr + 1, i + 1);
4142 #endif
4143       }
4144     }
4145
4146     if (player->connected && player->present)
4147       player->mapped = TRUE;
4148   }
4149
4150 #if DEBUG_INIT_PLAYER
4151   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4152 #endif
4153
4154 #else
4155
4156   // check if any connected player was not found in playfield
4157   for (i = 0; i < MAX_PLAYERS; i++)
4158   {
4159     struct PlayerInfo *player = &stored_player[i];
4160
4161     if (player->connected && !player->present)
4162     {
4163       for (j = 0; j < MAX_PLAYERS; j++)
4164       {
4165         struct PlayerInfo *field_player = &stored_player[j];
4166         int jx = field_player->jx, jy = field_player->jy;
4167
4168         // assign first free player found that is present in the playfield
4169         if (field_player->present && !field_player->connected)
4170         {
4171           player->present = TRUE;
4172           player->active = TRUE;
4173
4174           field_player->present = FALSE;
4175           field_player->active = FALSE;
4176
4177           player->initial_element = field_player->initial_element;
4178           player->artwork_element = field_player->artwork_element;
4179
4180           player->block_last_field       = field_player->block_last_field;
4181           player->block_delay_adjustment = field_player->block_delay_adjustment;
4182
4183           StorePlayer[jx][jy] = player->element_nr;
4184
4185           player->jx = player->last_jx = jx;
4186           player->jy = player->last_jy = jy;
4187
4188           break;
4189         }
4190       }
4191     }
4192   }
4193 #endif
4194
4195 #if 0
4196   Debug("game:init:player", "local_player->present == %d",
4197         local_player->present);
4198 #endif
4199
4200   // set focus to local player for network games, else to all players
4201   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4202   game.centered_player_nr_next = game.centered_player_nr;
4203   game.set_centered_player = FALSE;
4204   game.set_centered_player_wrap = FALSE;
4205
4206   if (network_playing && tape.recording)
4207   {
4208     // store client dependent player focus when recording network games
4209     tape.centered_player_nr_next = game.centered_player_nr_next;
4210     tape.set_centered_player = TRUE;
4211   }
4212
4213   if (tape.playing)
4214   {
4215     // when playing a tape, eliminate all players who do not participate
4216
4217 #if USE_NEW_PLAYER_ASSIGNMENTS
4218
4219     if (!game.team_mode)
4220     {
4221       for (i = 0; i < MAX_PLAYERS; i++)
4222       {
4223         if (stored_player[i].active &&
4224             !tape.player_participates[map_player_action[i]])
4225         {
4226           struct PlayerInfo *player = &stored_player[i];
4227           int jx = player->jx, jy = player->jy;
4228
4229 #if DEBUG_INIT_PLAYER
4230           Debug("game:init:player", "Removing player %d at (%d, %d)",
4231                 i + 1, jx, jy);
4232 #endif
4233
4234           player->active = FALSE;
4235           StorePlayer[jx][jy] = 0;
4236           Tile[jx][jy] = EL_EMPTY;
4237         }
4238       }
4239     }
4240
4241 #else
4242
4243     for (i = 0; i < MAX_PLAYERS; i++)
4244     {
4245       if (stored_player[i].active &&
4246           !tape.player_participates[i])
4247       {
4248         struct PlayerInfo *player = &stored_player[i];
4249         int jx = player->jx, jy = player->jy;
4250
4251         player->active = FALSE;
4252         StorePlayer[jx][jy] = 0;
4253         Tile[jx][jy] = EL_EMPTY;
4254       }
4255     }
4256 #endif
4257   }
4258   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4259   {
4260     // when in single player mode, eliminate all but the local player
4261
4262     for (i = 0; i < MAX_PLAYERS; i++)
4263     {
4264       struct PlayerInfo *player = &stored_player[i];
4265
4266       if (player->active && player != local_player)
4267       {
4268         int jx = player->jx, jy = player->jy;
4269
4270         player->active = FALSE;
4271         player->present = FALSE;
4272
4273         StorePlayer[jx][jy] = 0;
4274         Tile[jx][jy] = EL_EMPTY;
4275       }
4276     }
4277   }
4278
4279   for (i = 0; i < MAX_PLAYERS; i++)
4280     if (stored_player[i].active)
4281       game.players_still_needed++;
4282
4283   if (level.solved_by_one_player)
4284     game.players_still_needed = 1;
4285
4286   // when recording the game, store which players take part in the game
4287   if (tape.recording)
4288   {
4289 #if USE_NEW_PLAYER_ASSIGNMENTS
4290     for (i = 0; i < MAX_PLAYERS; i++)
4291       if (stored_player[i].connected)
4292         tape.player_participates[i] = TRUE;
4293 #else
4294     for (i = 0; i < MAX_PLAYERS; i++)
4295       if (stored_player[i].active)
4296         tape.player_participates[i] = TRUE;
4297 #endif
4298   }
4299
4300 #if DEBUG_INIT_PLAYER
4301   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4302 #endif
4303
4304   if (BorderElement == EL_EMPTY)
4305   {
4306     SBX_Left = 0;
4307     SBX_Right = lev_fieldx - SCR_FIELDX;
4308     SBY_Upper = 0;
4309     SBY_Lower = lev_fieldy - SCR_FIELDY;
4310   }
4311   else
4312   {
4313     SBX_Left = -1;
4314     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4315     SBY_Upper = -1;
4316     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4317   }
4318
4319   if (full_lev_fieldx <= SCR_FIELDX)
4320     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4321   if (full_lev_fieldy <= SCR_FIELDY)
4322     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4323
4324   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4325     SBX_Left--;
4326   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4327     SBY_Upper--;
4328
4329   // if local player not found, look for custom element that might create
4330   // the player (make some assumptions about the right custom element)
4331   if (!local_player->present)
4332   {
4333     int start_x = 0, start_y = 0;
4334     int found_rating = 0;
4335     int found_element = EL_UNDEFINED;
4336     int player_nr = local_player->index_nr;
4337
4338     SCAN_PLAYFIELD(x, y)
4339     {
4340       int element = Tile[x][y];
4341       int content;
4342       int xx, yy;
4343       boolean is_player;
4344
4345       if (level.use_start_element[player_nr] &&
4346           level.start_element[player_nr] == element &&
4347           found_rating < 4)
4348       {
4349         start_x = x;
4350         start_y = y;
4351
4352         found_rating = 4;
4353         found_element = element;
4354       }
4355
4356       if (!IS_CUSTOM_ELEMENT(element))
4357         continue;
4358
4359       if (CAN_CHANGE(element))
4360       {
4361         for (i = 0; i < element_info[element].num_change_pages; i++)
4362         {
4363           // check for player created from custom element as single target
4364           content = element_info[element].change_page[i].target_element;
4365           is_player = IS_PLAYER_ELEMENT(content);
4366
4367           if (is_player && (found_rating < 3 ||
4368                             (found_rating == 3 && element < found_element)))
4369           {
4370             start_x = x;
4371             start_y = y;
4372
4373             found_rating = 3;
4374             found_element = element;
4375           }
4376         }
4377       }
4378
4379       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4380       {
4381         // check for player created from custom element as explosion content
4382         content = element_info[element].content.e[xx][yy];
4383         is_player = IS_PLAYER_ELEMENT(content);
4384
4385         if (is_player && (found_rating < 2 ||
4386                           (found_rating == 2 && element < found_element)))
4387         {
4388           start_x = x + xx - 1;
4389           start_y = y + yy - 1;
4390
4391           found_rating = 2;
4392           found_element = element;
4393         }
4394
4395         if (!CAN_CHANGE(element))
4396           continue;
4397
4398         for (i = 0; i < element_info[element].num_change_pages; i++)
4399         {
4400           // check for player created from custom element as extended target
4401           content =
4402             element_info[element].change_page[i].target_content.e[xx][yy];
4403
4404           is_player = IS_PLAYER_ELEMENT(content);
4405
4406           if (is_player && (found_rating < 1 ||
4407                             (found_rating == 1 && element < found_element)))
4408           {
4409             start_x = x + xx - 1;
4410             start_y = y + yy - 1;
4411
4412             found_rating = 1;
4413             found_element = element;
4414           }
4415         }
4416       }
4417     }
4418
4419     scroll_x = SCROLL_POSITION_X(start_x);
4420     scroll_y = SCROLL_POSITION_Y(start_y);
4421   }
4422   else
4423   {
4424     scroll_x = SCROLL_POSITION_X(local_player->jx);
4425     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4426   }
4427
4428   // !!! FIX THIS (START) !!!
4429   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4430   {
4431     InitGameEngine_EM();
4432   }
4433   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4434   {
4435     InitGameEngine_SP();
4436   }
4437   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4438   {
4439     InitGameEngine_MM();
4440   }
4441   else
4442   {
4443     DrawLevel(REDRAW_FIELD);
4444     DrawAllPlayers();
4445
4446     // after drawing the level, correct some elements
4447     if (game.timegate_time_left == 0)
4448       CloseAllOpenTimegates();
4449   }
4450
4451   // blit playfield from scroll buffer to normal back buffer for fading in
4452   BlitScreenToBitmap(backbuffer);
4453   // !!! FIX THIS (END) !!!
4454
4455   DrawMaskedBorder(fade_mask);
4456
4457   FadeIn(fade_mask);
4458
4459 #if 1
4460   // full screen redraw is required at this point in the following cases:
4461   // - special editor door undrawn when game was started from level editor
4462   // - drawing area (playfield) was changed and has to be removed completely
4463   redraw_mask = REDRAW_ALL;
4464   BackToFront();
4465 #endif
4466
4467   if (!game.restart_level)
4468   {
4469     // copy default game door content to main double buffer
4470
4471     // !!! CHECK AGAIN !!!
4472     SetPanelBackground();
4473     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4474     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4475   }
4476
4477   SetPanelBackground();
4478   SetDrawBackgroundMask(REDRAW_DOOR_1);
4479
4480   UpdateAndDisplayGameControlValues();
4481
4482   if (!game.restart_level)
4483   {
4484     UnmapGameButtons();
4485     UnmapTapeButtons();
4486
4487     FreeGameButtons();
4488     CreateGameButtons();
4489
4490     MapGameButtons();
4491     MapTapeButtons();
4492
4493     // copy actual game door content to door double buffer for OpenDoor()
4494     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4495
4496     OpenDoor(DOOR_OPEN_ALL);
4497
4498     KeyboardAutoRepeatOffUnlessAutoplay();
4499
4500 #if DEBUG_INIT_PLAYER
4501     DebugPrintPlayerStatus("Player status (final)");
4502 #endif
4503   }
4504
4505   UnmapAllGadgets();
4506
4507   MapGameButtons();
4508   MapTapeButtons();
4509
4510   if (!game.restart_level && !tape.playing)
4511   {
4512     LevelStats_incPlayed(level_nr);
4513
4514     SaveLevelSetup_SeriesInfo();
4515   }
4516
4517   game.restart_level = FALSE;
4518
4519   game.request_active = FALSE;
4520   game.request_active_or_moving = FALSE;
4521
4522   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4523     InitGameActions_MM();
4524
4525   SaveEngineSnapshotToListInitial();
4526
4527   if (!game.restart_level)
4528   {
4529     PlaySound(SND_GAME_STARTING);
4530
4531     if (setup.sound_music)
4532       PlayLevelMusic();
4533   }
4534
4535   SetPlayfieldMouseCursorEnabled(!game.use_mouse_actions);
4536 }
4537
4538 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4539                         int actual_player_x, int actual_player_y)
4540 {
4541   // this is used for non-R'n'D game engines to update certain engine values
4542
4543   // needed to determine if sounds are played within the visible screen area
4544   scroll_x = actual_scroll_x;
4545   scroll_y = actual_scroll_y;
4546
4547   // needed to get player position for "follow finger" playing input method
4548   local_player->jx = actual_player_x;
4549   local_player->jy = actual_player_y;
4550 }
4551
4552 void InitMovDir(int x, int y)
4553 {
4554   int i, element = Tile[x][y];
4555   static int xy[4][2] =
4556   {
4557     {  0, +1 },
4558     { +1,  0 },
4559     {  0, -1 },
4560     { -1,  0 }
4561   };
4562   static int direction[3][4] =
4563   {
4564     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4565     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4566     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4567   };
4568
4569   switch (element)
4570   {
4571     case EL_BUG_RIGHT:
4572     case EL_BUG_UP:
4573     case EL_BUG_LEFT:
4574     case EL_BUG_DOWN:
4575       Tile[x][y] = EL_BUG;
4576       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4577       break;
4578
4579     case EL_SPACESHIP_RIGHT:
4580     case EL_SPACESHIP_UP:
4581     case EL_SPACESHIP_LEFT:
4582     case EL_SPACESHIP_DOWN:
4583       Tile[x][y] = EL_SPACESHIP;
4584       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4585       break;
4586
4587     case EL_BD_BUTTERFLY_RIGHT:
4588     case EL_BD_BUTTERFLY_UP:
4589     case EL_BD_BUTTERFLY_LEFT:
4590     case EL_BD_BUTTERFLY_DOWN:
4591       Tile[x][y] = EL_BD_BUTTERFLY;
4592       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4593       break;
4594
4595     case EL_BD_FIREFLY_RIGHT:
4596     case EL_BD_FIREFLY_UP:
4597     case EL_BD_FIREFLY_LEFT:
4598     case EL_BD_FIREFLY_DOWN:
4599       Tile[x][y] = EL_BD_FIREFLY;
4600       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4601       break;
4602
4603     case EL_PACMAN_RIGHT:
4604     case EL_PACMAN_UP:
4605     case EL_PACMAN_LEFT:
4606     case EL_PACMAN_DOWN:
4607       Tile[x][y] = EL_PACMAN;
4608       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4609       break;
4610
4611     case EL_YAMYAM_LEFT:
4612     case EL_YAMYAM_RIGHT:
4613     case EL_YAMYAM_UP:
4614     case EL_YAMYAM_DOWN:
4615       Tile[x][y] = EL_YAMYAM;
4616       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4617       break;
4618
4619     case EL_SP_SNIKSNAK:
4620       MovDir[x][y] = MV_UP;
4621       break;
4622
4623     case EL_SP_ELECTRON:
4624       MovDir[x][y] = MV_LEFT;
4625       break;
4626
4627     case EL_MOLE_LEFT:
4628     case EL_MOLE_RIGHT:
4629     case EL_MOLE_UP:
4630     case EL_MOLE_DOWN:
4631       Tile[x][y] = EL_MOLE;
4632       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4633       break;
4634
4635     case EL_SPRING_LEFT:
4636     case EL_SPRING_RIGHT:
4637       Tile[x][y] = EL_SPRING;
4638       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4639       break;
4640
4641     default:
4642       if (IS_CUSTOM_ELEMENT(element))
4643       {
4644         struct ElementInfo *ei = &element_info[element];
4645         int move_direction_initial = ei->move_direction_initial;
4646         int move_pattern = ei->move_pattern;
4647
4648         if (move_direction_initial == MV_START_PREVIOUS)
4649         {
4650           if (MovDir[x][y] != MV_NONE)
4651             return;
4652
4653           move_direction_initial = MV_START_AUTOMATIC;
4654         }
4655
4656         if (move_direction_initial == MV_START_RANDOM)
4657           MovDir[x][y] = 1 << RND(4);
4658         else if (move_direction_initial & MV_ANY_DIRECTION)
4659           MovDir[x][y] = move_direction_initial;
4660         else if (move_pattern == MV_ALL_DIRECTIONS ||
4661                  move_pattern == MV_TURNING_LEFT ||
4662                  move_pattern == MV_TURNING_RIGHT ||
4663                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4664                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4665                  move_pattern == MV_TURNING_RANDOM)
4666           MovDir[x][y] = 1 << RND(4);
4667         else if (move_pattern == MV_HORIZONTAL)
4668           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4669         else if (move_pattern == MV_VERTICAL)
4670           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4671         else if (move_pattern & MV_ANY_DIRECTION)
4672           MovDir[x][y] = element_info[element].move_pattern;
4673         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4674                  move_pattern == MV_ALONG_RIGHT_SIDE)
4675         {
4676           // use random direction as default start direction
4677           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4678             MovDir[x][y] = 1 << RND(4);
4679
4680           for (i = 0; i < NUM_DIRECTIONS; i++)
4681           {
4682             int x1 = x + xy[i][0];
4683             int y1 = y + xy[i][1];
4684
4685             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4686             {
4687               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4688                 MovDir[x][y] = direction[0][i];
4689               else
4690                 MovDir[x][y] = direction[1][i];
4691
4692               break;
4693             }
4694           }
4695         }                
4696       }
4697       else
4698       {
4699         MovDir[x][y] = 1 << RND(4);
4700
4701         if (element != EL_BUG &&
4702             element != EL_SPACESHIP &&
4703             element != EL_BD_BUTTERFLY &&
4704             element != EL_BD_FIREFLY)
4705           break;
4706
4707         for (i = 0; i < NUM_DIRECTIONS; i++)
4708         {
4709           int x1 = x + xy[i][0];
4710           int y1 = y + xy[i][1];
4711
4712           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4713           {
4714             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4715             {
4716               MovDir[x][y] = direction[0][i];
4717               break;
4718             }
4719             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4720                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4721             {
4722               MovDir[x][y] = direction[1][i];
4723               break;
4724             }
4725           }
4726         }
4727       }
4728       break;
4729   }
4730
4731   GfxDir[x][y] = MovDir[x][y];
4732 }
4733
4734 void InitAmoebaNr(int x, int y)
4735 {
4736   int i;
4737   int group_nr = AmoebaNeighbourNr(x, y);
4738
4739   if (group_nr == 0)
4740   {
4741     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4742     {
4743       if (AmoebaCnt[i] == 0)
4744       {
4745         group_nr = i;
4746         break;
4747       }
4748     }
4749   }
4750
4751   AmoebaNr[x][y] = group_nr;
4752   AmoebaCnt[group_nr]++;
4753   AmoebaCnt2[group_nr]++;
4754 }
4755
4756 static void LevelSolved_SetFinalGameValues(void)
4757 {
4758   game.time_final = (game.no_level_time_limit ? TimePlayed : TimeLeft);
4759   game.score_time_final = (level.use_step_counter ? TimePlayed :
4760                            TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4761
4762   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4763                       game_em.lev->score :
4764                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4765                       game_mm.score :
4766                       game.score);
4767
4768   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4769                        MM_HEALTH(game_mm.laser_overload_value) :
4770                        game.health);
4771
4772   game.LevelSolved_CountingTime = game.time_final;
4773   game.LevelSolved_CountingScore = game.score_final;
4774   game.LevelSolved_CountingHealth = game.health_final;
4775 }
4776
4777 static void LevelSolved_DisplayFinalGameValues(int time, int score, int health)
4778 {
4779   game.LevelSolved_CountingTime = time;
4780   game.LevelSolved_CountingScore = score;
4781   game.LevelSolved_CountingHealth = health;
4782
4783   game_panel_controls[GAME_PANEL_TIME].value = time;
4784   game_panel_controls[GAME_PANEL_SCORE].value = score;
4785   game_panel_controls[GAME_PANEL_HEALTH].value = health;
4786
4787   DisplayGameControlValues();
4788 }
4789
4790 static void LevelSolved(void)
4791 {
4792   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4793       game.players_still_needed > 0)
4794     return;
4795
4796   game.LevelSolved = TRUE;
4797   game.GameOver = TRUE;
4798
4799   tape.solved = TRUE;
4800
4801   // needed here to display correct panel values while player walks into exit
4802   LevelSolved_SetFinalGameValues();
4803 }
4804
4805 void GameWon(void)
4806 {
4807   static int time_count_steps;
4808   static int time, time_final;
4809   static float score, score_final; // needed for time score < 10 for 10 seconds
4810   static int health, health_final;
4811   static int game_over_delay_1 = 0;
4812   static int game_over_delay_2 = 0;
4813   static int game_over_delay_3 = 0;
4814   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4815   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4816
4817   if (!game.LevelSolved_GameWon)
4818   {
4819     int i;
4820
4821     // do not start end game actions before the player stops moving (to exit)
4822     if (local_player->active && local_player->MovPos)
4823       return;
4824
4825     // calculate final game values after player finished walking into exit
4826     LevelSolved_SetFinalGameValues();
4827
4828     game.LevelSolved_GameWon = TRUE;
4829     game.LevelSolved_SaveTape = tape.recording;
4830     game.LevelSolved_SaveScore = !tape.playing;
4831
4832     if (!tape.playing)
4833     {
4834       LevelStats_incSolved(level_nr);
4835
4836       SaveLevelSetup_SeriesInfo();
4837     }
4838
4839     if (tape.auto_play)         // tape might already be stopped here
4840       tape.auto_play_level_solved = TRUE;
4841
4842     TapeStop();
4843
4844     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4845     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4846     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4847
4848     time = time_final = game.time_final;
4849     score = score_final = game.score_final;
4850     health = health_final = game.health_final;
4851
4852     // update game panel values before (delayed) counting of score (if any)
4853     LevelSolved_DisplayFinalGameValues(time, score, health);
4854
4855     // if level has time score defined, calculate new final game values
4856     if (time_score > 0)
4857     {
4858       int time_final_max = 999;
4859       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4860       int time_frames = 0;
4861       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4862       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4863
4864       if (TimeLeft > 0)
4865       {
4866         time_final = 0;
4867         time_frames = time_frames_left;
4868       }
4869       else if (game.no_level_time_limit && TimePlayed < time_final_max)
4870       {
4871         time_final = time_final_max;
4872         time_frames = time_frames_final_max - time_frames_played;
4873       }
4874
4875       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4876
4877       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4878
4879       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4880       {
4881         health_final = 0;
4882         score_final += health * time_score;
4883       }
4884
4885       game.score_final = score_final;
4886       game.health_final = health_final;
4887     }
4888
4889     // if not counting score after game, immediately update game panel values
4890     if (level_editor_test_game || !setup.count_score_after_game)
4891     {
4892       time = time_final;
4893       score = score_final;
4894
4895       LevelSolved_DisplayFinalGameValues(time, score, health);
4896     }
4897
4898     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4899     {
4900       // check if last player has left the level
4901       if (game.exit_x >= 0 &&
4902           game.exit_y >= 0)
4903       {
4904         int x = game.exit_x;
4905         int y = game.exit_y;
4906         int element = Tile[x][y];
4907
4908         // close exit door after last player
4909         if ((game.all_players_gone &&
4910              (element == EL_EXIT_OPEN ||
4911               element == EL_SP_EXIT_OPEN ||
4912               element == EL_STEEL_EXIT_OPEN)) ||
4913             element == EL_EM_EXIT_OPEN ||
4914             element == EL_EM_STEEL_EXIT_OPEN)
4915         {
4916
4917           Tile[x][y] =
4918             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4919              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4920              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4921              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4922              EL_EM_STEEL_EXIT_CLOSING);
4923
4924           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4925         }
4926
4927         // player disappears
4928         DrawLevelField(x, y);
4929       }
4930
4931       for (i = 0; i < MAX_PLAYERS; i++)
4932       {
4933         struct PlayerInfo *player = &stored_player[i];
4934
4935         if (player->present)
4936         {
4937           RemovePlayer(player);
4938
4939           // player disappears
4940           DrawLevelField(player->jx, player->jy);
4941         }
4942       }
4943     }
4944
4945     PlaySound(SND_GAME_WINNING);
4946   }
4947
4948   if (setup.count_score_after_game)
4949   {
4950     if (time != time_final)
4951     {
4952       if (game_over_delay_1 > 0)
4953       {
4954         game_over_delay_1--;
4955
4956         return;
4957       }
4958
4959       int time_to_go = ABS(time_final - time);
4960       int time_count_dir = (time < time_final ? +1 : -1);
4961
4962       if (time_to_go < time_count_steps)
4963         time_count_steps = 1;
4964
4965       time  += time_count_steps * time_count_dir;
4966       score += time_count_steps * time_score;
4967
4968       // set final score to correct rounding differences after counting score
4969       if (time == time_final)
4970         score = score_final;
4971
4972       LevelSolved_DisplayFinalGameValues(time, score, health);
4973
4974       if (time == time_final)
4975         StopSound(SND_GAME_LEVELTIME_BONUS);
4976       else if (setup.sound_loops)
4977         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4978       else
4979         PlaySound(SND_GAME_LEVELTIME_BONUS);
4980
4981       return;
4982     }
4983
4984     if (health != health_final)
4985     {
4986       if (game_over_delay_2 > 0)
4987       {
4988         game_over_delay_2--;
4989
4990         return;
4991       }
4992
4993       int health_count_dir = (health < health_final ? +1 : -1);
4994
4995       health += health_count_dir;
4996       score  += time_score;
4997
4998       LevelSolved_DisplayFinalGameValues(time, score, health);
4999
5000       if (health == health_final)
5001         StopSound(SND_GAME_LEVELTIME_BONUS);
5002       else if (setup.sound_loops)
5003         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5004       else
5005         PlaySound(SND_GAME_LEVELTIME_BONUS);
5006
5007       return;
5008     }
5009   }
5010
5011   game.panel.active = FALSE;
5012
5013   if (game_over_delay_3 > 0)
5014   {
5015     game_over_delay_3--;
5016
5017     return;
5018   }
5019
5020   GameEnd();
5021 }
5022
5023 void GameEnd(void)
5024 {
5025   // used instead of "level_nr" (needed for network games)
5026   int last_level_nr = levelset.level_nr;
5027   boolean tape_saved = FALSE;
5028
5029   game.LevelSolved_GameEnd = TRUE;
5030
5031   if (game.LevelSolved_SaveTape && !score_info_tape_play)
5032   {
5033     // make sure that request dialog to save tape does not open door again
5034     if (!global.use_envelope_request)
5035       CloseDoor(DOOR_CLOSE_1);
5036
5037     // ask to save tape
5038     tape_saved = SaveTapeChecked_LevelSolved(tape.level_nr);
5039
5040     // set unique basename for score tape (also saved in high score table)
5041     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
5042   }
5043
5044   // if no tape is to be saved, close both doors simultaneously
5045   CloseDoor(DOOR_CLOSE_ALL);
5046
5047   if (level_editor_test_game || score_info_tape_play)
5048   {
5049     SetGameStatus(GAME_MODE_MAIN);
5050
5051     DrawMainMenu();
5052
5053     return;
5054   }
5055
5056   if (!game.LevelSolved_SaveScore)
5057   {
5058     SetGameStatus(GAME_MODE_MAIN);
5059
5060     DrawMainMenu();
5061
5062     return;
5063   }
5064
5065   if (level_nr == leveldir_current->handicap_level)
5066   {
5067     leveldir_current->handicap_level++;
5068
5069     SaveLevelSetup_SeriesInfo();
5070   }
5071
5072   // save score and score tape before potentially erasing tape below
5073   NewHighScore(last_level_nr, tape_saved);
5074
5075   if (setup.increment_levels &&
5076       level_nr < leveldir_current->last_level &&
5077       !network_playing)
5078   {
5079     level_nr++;         // advance to next level
5080     TapeErase();        // start with empty tape
5081
5082     if (setup.auto_play_next_level)
5083     {
5084       scores.continue_playing = TRUE;
5085       scores.next_level_nr = level_nr;
5086
5087       LoadLevel(level_nr);
5088
5089       SaveLevelSetup_SeriesInfo();
5090     }
5091   }
5092
5093   if (scores.last_added >= 0 && setup.show_scores_after_game)
5094   {
5095     SetGameStatus(GAME_MODE_SCORES);
5096
5097     DrawHallOfFame(last_level_nr);
5098   }
5099   else if (scores.continue_playing)
5100   {
5101     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5102   }
5103   else
5104   {
5105     SetGameStatus(GAME_MODE_MAIN);
5106
5107     DrawMainMenu();
5108   }
5109 }
5110
5111 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5112                          boolean one_score_entry_per_name)
5113 {
5114   int i;
5115
5116   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5117     return -1;
5118
5119   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5120   {
5121     struct ScoreEntry *entry = &list->entry[i];
5122     boolean score_is_better = (new_entry->score >  entry->score);
5123     boolean score_is_equal  = (new_entry->score == entry->score);
5124     boolean time_is_better  = (new_entry->time  <  entry->time);
5125     boolean time_is_equal   = (new_entry->time  == entry->time);
5126     boolean better_by_score = (score_is_better ||
5127                                (score_is_equal && time_is_better));
5128     boolean better_by_time  = (time_is_better ||
5129                                (time_is_equal && score_is_better));
5130     boolean is_better = (level.rate_time_over_score ? better_by_time :
5131                          better_by_score);
5132     boolean entry_is_empty = (entry->score == 0 &&
5133                               entry->time == 0);
5134
5135     // prevent adding server score entries if also existing in local score file
5136     // (special case: historic score entries have an empty tape basename entry)
5137     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5138         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5139     {
5140       // add fields from server score entry not stored in local score entry
5141       // (currently, this means setting platform, version and country fields;
5142       // in rare cases, this may also correct an invalid score value, as
5143       // historic scores might have been truncated to 16-bit values locally)
5144       *entry = *new_entry;
5145
5146       return -1;
5147     }
5148
5149     if (is_better || entry_is_empty)
5150     {
5151       // player has made it to the hall of fame
5152
5153       if (i < MAX_SCORE_ENTRIES - 1)
5154       {
5155         int m = MAX_SCORE_ENTRIES - 1;
5156         int l;
5157
5158         if (one_score_entry_per_name)
5159         {
5160           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5161             if (strEqual(list->entry[l].name, new_entry->name))
5162               m = l;
5163
5164           if (m == i)   // player's new highscore overwrites his old one
5165             goto put_into_list;
5166         }
5167
5168         for (l = m; l > i; l--)
5169           list->entry[l] = list->entry[l - 1];
5170       }
5171
5172       put_into_list:
5173
5174       *entry = *new_entry;
5175
5176       return i;
5177     }
5178     else if (one_score_entry_per_name &&
5179              strEqual(entry->name, new_entry->name))
5180     {
5181       // player already in high score list with better score or time
5182
5183       return -1;
5184     }
5185   }
5186
5187   // special case: new score is beyond the last high score list position
5188   return MAX_SCORE_ENTRIES;
5189 }
5190
5191 void NewHighScore(int level_nr, boolean tape_saved)
5192 {
5193   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5194   boolean one_per_name = FALSE;
5195
5196   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5197   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5198
5199   new_entry.score = game.score_final;
5200   new_entry.time = game.score_time_final;
5201
5202   LoadScore(level_nr);
5203
5204   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5205
5206   if (scores.last_added >= MAX_SCORE_ENTRIES)
5207   {
5208     scores.last_added = MAX_SCORE_ENTRIES - 1;
5209     scores.force_last_added = TRUE;
5210
5211     scores.entry[scores.last_added] = new_entry;
5212
5213     // store last added local score entry (before merging server scores)
5214     scores.last_added_local = scores.last_added;
5215
5216     return;
5217   }
5218
5219   if (scores.last_added < 0)
5220     return;
5221
5222   SaveScore(level_nr);
5223
5224   // store last added local score entry (before merging server scores)
5225   scores.last_added_local = scores.last_added;
5226
5227   if (!game.LevelSolved_SaveTape)
5228     return;
5229
5230   SaveScoreTape(level_nr);
5231
5232   if (setup.ask_for_using_api_server)
5233   {
5234     setup.use_api_server =
5235       Request("Upload your score and tape to the high score server?", REQ_ASK);
5236
5237     if (!setup.use_api_server)
5238       Request("Not using high score server! Use setup menu to enable again!",
5239               REQ_CONFIRM);
5240
5241     runtime.use_api_server = setup.use_api_server;
5242
5243     // after asking for using API server once, do not ask again
5244     setup.ask_for_using_api_server = FALSE;
5245
5246     SaveSetup_ServerSetup();
5247   }
5248
5249   SaveServerScore(level_nr, tape_saved);
5250 }
5251
5252 void MergeServerScore(void)
5253 {
5254   struct ScoreEntry last_added_entry;
5255   boolean one_per_name = FALSE;
5256   int i;
5257
5258   if (scores.last_added >= 0)
5259     last_added_entry = scores.entry[scores.last_added];
5260
5261   for (i = 0; i < server_scores.num_entries; i++)
5262   {
5263     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5264
5265     if (pos >= 0 && pos <= scores.last_added)
5266       scores.last_added++;
5267   }
5268
5269   if (scores.last_added >= MAX_SCORE_ENTRIES)
5270   {
5271     scores.last_added = MAX_SCORE_ENTRIES - 1;
5272     scores.force_last_added = TRUE;
5273
5274     scores.entry[scores.last_added] = last_added_entry;
5275   }
5276 }
5277
5278 static int getElementMoveStepsizeExt(int x, int y, int direction)
5279 {
5280   int element = Tile[x][y];
5281   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5282   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5283   int horiz_move = (dx != 0);
5284   int sign = (horiz_move ? dx : dy);
5285   int step = sign * element_info[element].move_stepsize;
5286
5287   // special values for move stepsize for spring and things on conveyor belt
5288   if (horiz_move)
5289   {
5290     if (CAN_FALL(element) &&
5291         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5292       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5293     else if (element == EL_SPRING)
5294       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5295   }
5296
5297   return step;
5298 }
5299
5300 static int getElementMoveStepsize(int x, int y)
5301 {
5302   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5303 }
5304
5305 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5306 {
5307   if (player->GfxAction != action || player->GfxDir != dir)
5308   {
5309     player->GfxAction = action;
5310     player->GfxDir = dir;
5311     player->Frame = 0;
5312     player->StepFrame = 0;
5313   }
5314 }
5315
5316 static void ResetGfxFrame(int x, int y)
5317 {
5318   // profiling showed that "autotest" spends 10~20% of its time in this function
5319   if (DrawingDeactivatedField())
5320     return;
5321
5322   int element = Tile[x][y];
5323   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5324
5325   if (graphic_info[graphic].anim_global_sync)
5326     GfxFrame[x][y] = FrameCounter;
5327   else if (graphic_info[graphic].anim_global_anim_sync)
5328     GfxFrame[x][y] = getGlobalAnimSyncFrame();
5329   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5330     GfxFrame[x][y] = CustomValue[x][y];
5331   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5332     GfxFrame[x][y] = element_info[element].collect_score;
5333   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5334     GfxFrame[x][y] = ChangeDelay[x][y];
5335 }
5336
5337 static void ResetGfxAnimation(int x, int y)
5338 {
5339   GfxAction[x][y] = ACTION_DEFAULT;
5340   GfxDir[x][y] = MovDir[x][y];
5341   GfxFrame[x][y] = 0;
5342
5343   ResetGfxFrame(x, y);
5344 }
5345
5346 static void ResetRandomAnimationValue(int x, int y)
5347 {
5348   GfxRandom[x][y] = INIT_GFX_RANDOM();
5349 }
5350
5351 static void InitMovingField(int x, int y, int direction)
5352 {
5353   int element = Tile[x][y];
5354   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5355   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5356   int newx = x + dx;
5357   int newy = y + dy;
5358   boolean is_moving_before, is_moving_after;
5359
5360   // check if element was/is moving or being moved before/after mode change
5361   is_moving_before = (WasJustMoving[x][y] != 0);
5362   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5363
5364   // reset animation only for moving elements which change direction of moving
5365   // or which just started or stopped moving
5366   // (else CEs with property "can move" / "not moving" are reset each frame)
5367   if (is_moving_before != is_moving_after ||
5368       direction != MovDir[x][y])
5369     ResetGfxAnimation(x, y);
5370
5371   MovDir[x][y] = direction;
5372   GfxDir[x][y] = direction;
5373
5374   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5375                      direction == MV_DOWN && CAN_FALL(element) ?
5376                      ACTION_FALLING : ACTION_MOVING);
5377
5378   // this is needed for CEs with property "can move" / "not moving"
5379
5380   if (is_moving_after)
5381   {
5382     if (Tile[newx][newy] == EL_EMPTY)
5383       Tile[newx][newy] = EL_BLOCKED;
5384
5385     MovDir[newx][newy] = MovDir[x][y];
5386
5387     CustomValue[newx][newy] = CustomValue[x][y];
5388
5389     GfxFrame[newx][newy] = GfxFrame[x][y];
5390     GfxRandom[newx][newy] = GfxRandom[x][y];
5391     GfxAction[newx][newy] = GfxAction[x][y];
5392     GfxDir[newx][newy] = GfxDir[x][y];
5393   }
5394 }
5395
5396 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5397 {
5398   int direction = MovDir[x][y];
5399   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5400   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5401
5402   *goes_to_x = newx;
5403   *goes_to_y = newy;
5404 }
5405
5406 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5407 {
5408   int direction = MovDir[x][y];
5409   int oldx = x + (direction & MV_LEFT ? +1 : direction & MV_RIGHT ? -1 : 0);
5410   int oldy = y + (direction & MV_UP   ? +1 : direction & MV_DOWN  ? -1 : 0);
5411
5412   *comes_from_x = oldx;
5413   *comes_from_y = oldy;
5414 }
5415
5416 static int MovingOrBlocked2Element(int x, int y)
5417 {
5418   int element = Tile[x][y];
5419
5420   if (element == EL_BLOCKED)
5421   {
5422     int oldx, oldy;
5423
5424     Blocked2Moving(x, y, &oldx, &oldy);
5425
5426     return Tile[oldx][oldy];
5427   }
5428
5429   return element;
5430 }
5431
5432 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5433 {
5434   // like MovingOrBlocked2Element(), but if element is moving
5435   // and (x, y) is the field the moving element is just leaving,
5436   // return EL_BLOCKED instead of the element value
5437   int element = Tile[x][y];
5438
5439   if (IS_MOVING(x, y))
5440   {
5441     if (element == EL_BLOCKED)
5442     {
5443       int oldx, oldy;
5444
5445       Blocked2Moving(x, y, &oldx, &oldy);
5446       return Tile[oldx][oldy];
5447     }
5448     else
5449       return EL_BLOCKED;
5450   }
5451   else
5452     return element;
5453 }
5454
5455 static void RemoveField(int x, int y)
5456 {
5457   Tile[x][y] = EL_EMPTY;
5458
5459   MovPos[x][y] = 0;
5460   MovDir[x][y] = 0;
5461   MovDelay[x][y] = 0;
5462
5463   CustomValue[x][y] = 0;
5464
5465   AmoebaNr[x][y] = 0;
5466   ChangeDelay[x][y] = 0;
5467   ChangePage[x][y] = -1;
5468   Pushed[x][y] = FALSE;
5469
5470   GfxElement[x][y] = EL_UNDEFINED;
5471   GfxAction[x][y] = ACTION_DEFAULT;
5472   GfxDir[x][y] = MV_NONE;
5473 }
5474
5475 static void RemoveMovingField(int x, int y)
5476 {
5477   int oldx = x, oldy = y, newx = x, newy = y;
5478   int element = Tile[x][y];
5479   int next_element = EL_UNDEFINED;
5480
5481   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5482     return;
5483
5484   if (IS_MOVING(x, y))
5485   {
5486     Moving2Blocked(x, y, &newx, &newy);
5487
5488     if (Tile[newx][newy] != EL_BLOCKED)
5489     {
5490       // element is moving, but target field is not free (blocked), but
5491       // already occupied by something different (example: acid pool);
5492       // in this case, only remove the moving field, but not the target
5493
5494       RemoveField(oldx, oldy);
5495
5496       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5497
5498       TEST_DrawLevelField(oldx, oldy);
5499
5500       return;
5501     }
5502   }
5503   else if (element == EL_BLOCKED)
5504   {
5505     Blocked2Moving(x, y, &oldx, &oldy);
5506     if (!IS_MOVING(oldx, oldy))
5507       return;
5508   }
5509
5510   if (element == EL_BLOCKED &&
5511       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5512        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5513        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5514        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5515        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5516        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5517     next_element = get_next_element(Tile[oldx][oldy]);
5518
5519   RemoveField(oldx, oldy);
5520   RemoveField(newx, newy);
5521
5522   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5523
5524   if (next_element != EL_UNDEFINED)
5525     Tile[oldx][oldy] = next_element;
5526
5527   TEST_DrawLevelField(oldx, oldy);
5528   TEST_DrawLevelField(newx, newy);
5529 }
5530
5531 void DrawDynamite(int x, int y)
5532 {
5533   int sx = SCREENX(x), sy = SCREENY(y);
5534   int graphic = el2img(Tile[x][y]);
5535   int frame;
5536
5537   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5538     return;
5539
5540   if (IS_WALKABLE_INSIDE(Back[x][y]))
5541     return;
5542
5543   if (Back[x][y])
5544     DrawLevelElement(x, y, Back[x][y]);
5545   else if (Store[x][y])
5546     DrawLevelElement(x, y, Store[x][y]);
5547   else if (game.use_masked_elements)
5548     DrawLevelElement(x, y, EL_EMPTY);
5549
5550   frame = getGraphicAnimationFrameXY(graphic, x, y);
5551
5552   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5553     DrawGraphicThruMask(sx, sy, graphic, frame);
5554   else
5555     DrawGraphic(sx, sy, graphic, frame);
5556 }
5557
5558 static void CheckDynamite(int x, int y)
5559 {
5560   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5561   {
5562     MovDelay[x][y]--;
5563
5564     if (MovDelay[x][y] != 0)
5565     {
5566       DrawDynamite(x, y);
5567       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5568
5569       return;
5570     }
5571   }
5572
5573   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5574
5575   Bang(x, y);
5576 }
5577
5578 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5579 {
5580   boolean num_checked_players = 0;
5581   int i;
5582
5583   for (i = 0; i < MAX_PLAYERS; i++)
5584   {
5585     if (stored_player[i].active)
5586     {
5587       int sx = stored_player[i].jx;
5588       int sy = stored_player[i].jy;
5589
5590       if (num_checked_players == 0)
5591       {
5592         *sx1 = *sx2 = sx;
5593         *sy1 = *sy2 = sy;
5594       }
5595       else
5596       {
5597         *sx1 = MIN(*sx1, sx);
5598         *sy1 = MIN(*sy1, sy);
5599         *sx2 = MAX(*sx2, sx);
5600         *sy2 = MAX(*sy2, sy);
5601       }
5602
5603       num_checked_players++;
5604     }
5605   }
5606 }
5607
5608 static boolean checkIfAllPlayersFitToScreen_RND(void)
5609 {
5610   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5611
5612   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5613
5614   return (sx2 - sx1 < SCR_FIELDX &&
5615           sy2 - sy1 < SCR_FIELDY);
5616 }
5617
5618 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5619 {
5620   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5621
5622   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5623
5624   *sx = (sx1 + sx2) / 2;
5625   *sy = (sy1 + sy2) / 2;
5626 }
5627
5628 static void DrawRelocateScreen(int old_x, int old_y, int x, int y,
5629                                boolean center_screen, boolean quick_relocation)
5630 {
5631   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5632   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5633   boolean no_delay = (tape.warp_forward);
5634   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5635   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5636   int new_scroll_x, new_scroll_y;
5637
5638   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5639   {
5640     // case 1: quick relocation inside visible screen (without scrolling)
5641
5642     RedrawPlayfield();
5643
5644     return;
5645   }
5646
5647   if (!level.shifted_relocation || center_screen)
5648   {
5649     // relocation _with_ centering of screen
5650
5651     new_scroll_x = SCROLL_POSITION_X(x);
5652     new_scroll_y = SCROLL_POSITION_Y(y);
5653   }
5654   else
5655   {
5656     // relocation _without_ centering of screen
5657
5658     int center_scroll_x = SCROLL_POSITION_X(old_x);
5659     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5660     int offset_x = x + (scroll_x - center_scroll_x);
5661     int offset_y = y + (scroll_y - center_scroll_y);
5662
5663     // for new screen position, apply previous offset to center position
5664     new_scroll_x = SCROLL_POSITION_X(offset_x);
5665     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5666   }
5667
5668   if (quick_relocation)
5669   {
5670     // case 2: quick relocation (redraw without visible scrolling)
5671
5672     scroll_x = new_scroll_x;
5673     scroll_y = new_scroll_y;
5674
5675     RedrawPlayfield();
5676
5677     return;
5678   }
5679
5680   // case 3: visible relocation (with scrolling to new position)
5681
5682   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5683
5684   SetVideoFrameDelay(wait_delay_value);
5685
5686   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5687   {
5688     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5689     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5690
5691     if (dx == 0 && dy == 0)             // no scrolling needed at all
5692       break;
5693
5694     scroll_x -= dx;
5695     scroll_y -= dy;
5696
5697     // set values for horizontal/vertical screen scrolling (half tile size)
5698     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5699     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5700     int pos_x = dx * TILEX / 2;
5701     int pos_y = dy * TILEY / 2;
5702     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5703     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5704
5705     ScrollLevel(dx, dy);
5706     DrawAllPlayers();
5707
5708     // scroll in two steps of half tile size to make things smoother
5709     BlitScreenToBitmapExt_RND(window, fx, fy);
5710
5711     // scroll second step to align at full tile size
5712     BlitScreenToBitmap(window);
5713   }
5714
5715   DrawAllPlayers();
5716   BackToFront();
5717
5718   SetVideoFrameDelay(frame_delay_value_old);
5719 }
5720
5721 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5722 {
5723   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5724   int player_nr = GET_PLAYER_NR(el_player);
5725   struct PlayerInfo *player = &stored_player[player_nr];
5726   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5727   boolean no_delay = (tape.warp_forward);
5728   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5729   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5730   int old_jx = player->jx;
5731   int old_jy = player->jy;
5732   int old_element = Tile[old_jx][old_jy];
5733   int element = Tile[jx][jy];
5734   boolean player_relocated = (old_jx != jx || old_jy != jy);
5735
5736   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5737   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5738   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5739   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5740   int leave_side_horiz = move_dir_horiz;
5741   int leave_side_vert  = move_dir_vert;
5742   int enter_side = enter_side_horiz | enter_side_vert;
5743   int leave_side = leave_side_horiz | leave_side_vert;
5744
5745   if (player->buried)           // do not reanimate dead player
5746     return;
5747
5748   if (!player_relocated)        // no need to relocate the player
5749     return;
5750
5751   if (IS_PLAYER(jx, jy))        // player already placed at new position
5752   {
5753     RemoveField(jx, jy);        // temporarily remove newly placed player
5754     DrawLevelField(jx, jy);
5755   }
5756
5757   if (player->present)
5758   {
5759     while (player->MovPos)
5760     {
5761       ScrollPlayer(player, SCROLL_GO_ON);
5762       ScrollScreen(NULL, SCROLL_GO_ON);
5763
5764       AdvanceFrameAndPlayerCounters(player->index_nr);
5765
5766       DrawPlayer(player);
5767
5768       BackToFront_WithFrameDelay(wait_delay_value);
5769     }
5770
5771     DrawPlayer(player);         // needed here only to cleanup last field
5772     DrawLevelField(player->jx, player->jy);     // remove player graphic
5773
5774     player->is_moving = FALSE;
5775   }
5776
5777   if (IS_CUSTOM_ELEMENT(old_element))
5778     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5779                                CE_LEFT_BY_PLAYER,
5780                                player->index_bit, leave_side);
5781
5782   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5783                                       CE_PLAYER_LEAVES_X,
5784                                       player->index_bit, leave_side);
5785
5786   Tile[jx][jy] = el_player;
5787   InitPlayerField(jx, jy, el_player, TRUE);
5788
5789   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5790      possible that the relocation target field did not contain a player element,
5791      but a walkable element, to which the new player was relocated -- in this
5792      case, restore that (already initialized!) element on the player field */
5793   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5794   {
5795     Tile[jx][jy] = element;     // restore previously existing element
5796   }
5797
5798   // only visually relocate centered player
5799   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy,
5800                      FALSE, level.instant_relocation);
5801
5802   TestIfPlayerTouchesBadThing(jx, jy);
5803   TestIfPlayerTouchesCustomElement(jx, jy);
5804
5805   if (IS_CUSTOM_ELEMENT(element))
5806     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5807                                player->index_bit, enter_side);
5808
5809   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5810                                       player->index_bit, enter_side);
5811
5812   if (player->is_switching)
5813   {
5814     /* ensure that relocation while still switching an element does not cause
5815        a new element to be treated as also switched directly after relocation
5816        (this is important for teleporter switches that teleport the player to
5817        a place where another teleporter switch is in the same direction, which
5818        would then incorrectly be treated as immediately switched before the
5819        direction key that caused the switch was released) */
5820
5821     player->switch_x += jx - old_jx;
5822     player->switch_y += jy - old_jy;
5823   }
5824 }
5825
5826 static void Explode(int ex, int ey, int phase, int mode)
5827 {
5828   int x, y;
5829   int last_phase;
5830   int border_element;
5831
5832   if (game.explosions_delayed)
5833   {
5834     ExplodeField[ex][ey] = mode;
5835     return;
5836   }
5837
5838   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5839   {
5840     int center_element = Tile[ex][ey];
5841     int ce_value = CustomValue[ex][ey];
5842     int ce_score = element_info[center_element].collect_score;
5843     int artwork_element, explosion_element;     // set these values later
5844
5845     // remove things displayed in background while burning dynamite
5846     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5847       Back[ex][ey] = 0;
5848
5849     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5850     {
5851       // put moving element to center field (and let it explode there)
5852       center_element = MovingOrBlocked2Element(ex, ey);
5853       RemoveMovingField(ex, ey);
5854       Tile[ex][ey] = center_element;
5855     }
5856
5857     // now "center_element" is finally determined -- set related values now
5858     artwork_element = center_element;           // for custom player artwork
5859     explosion_element = center_element;         // for custom player artwork
5860
5861     if (IS_PLAYER(ex, ey))
5862     {
5863       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5864
5865       artwork_element = stored_player[player_nr].artwork_element;
5866
5867       if (level.use_explosion_element[player_nr])
5868       {
5869         explosion_element = level.explosion_element[player_nr];
5870         artwork_element = explosion_element;
5871       }
5872     }
5873
5874     if (mode == EX_TYPE_NORMAL ||
5875         mode == EX_TYPE_CENTER ||
5876         mode == EX_TYPE_CROSS)
5877       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5878
5879     last_phase = element_info[explosion_element].explosion_delay + 1;
5880
5881     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5882     {
5883       int xx = x - ex + 1;
5884       int yy = y - ey + 1;
5885       int element;
5886
5887       if (!IN_LEV_FIELD(x, y) ||
5888           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5889           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5890         continue;
5891
5892       element = Tile[x][y];
5893
5894       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5895       {
5896         element = MovingOrBlocked2Element(x, y);
5897
5898         if (!IS_EXPLOSION_PROOF(element))
5899           RemoveMovingField(x, y);
5900       }
5901
5902       // indestructible elements can only explode in center (but not flames)
5903       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5904                                            mode == EX_TYPE_BORDER)) ||
5905           element == EL_FLAMES)
5906         continue;
5907
5908       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5909          behaviour, for example when touching a yamyam that explodes to rocks
5910          with active deadly shield, a rock is created under the player !!! */
5911       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5912 #if 0
5913       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5914           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5915            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5916 #else
5917       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5918 #endif
5919       {
5920         if (IS_ACTIVE_BOMB(element))
5921         {
5922           // re-activate things under the bomb like gate or penguin
5923           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5924           Back[x][y] = 0;
5925         }
5926
5927         continue;
5928       }
5929
5930       // save walkable background elements while explosion on same tile
5931       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5932           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5933         Back[x][y] = element;
5934
5935       // ignite explodable elements reached by other explosion
5936       if (element == EL_EXPLOSION)
5937         element = Store2[x][y];
5938
5939       if (AmoebaNr[x][y] &&
5940           (element == EL_AMOEBA_FULL ||
5941            element == EL_BD_AMOEBA ||
5942            element == EL_AMOEBA_GROWING))
5943       {
5944         AmoebaCnt[AmoebaNr[x][y]]--;
5945         AmoebaCnt2[AmoebaNr[x][y]]--;
5946       }
5947
5948       RemoveField(x, y);
5949
5950       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5951       {
5952         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5953
5954         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5955
5956         if (PLAYERINFO(ex, ey)->use_murphy)
5957           Store[x][y] = EL_EMPTY;
5958       }
5959
5960       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5961       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5962       else if (IS_PLAYER_ELEMENT(center_element))
5963         Store[x][y] = EL_EMPTY;
5964       else if (center_element == EL_YAMYAM)
5965         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5966       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5967         Store[x][y] = element_info[center_element].content.e[xx][yy];
5968 #if 1
5969       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5970       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5971       // otherwise) -- FIX THIS !!!
5972       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5973         Store[x][y] = element_info[element].content.e[1][1];
5974 #else
5975       else if (!CAN_EXPLODE(element))
5976         Store[x][y] = element_info[element].content.e[1][1];
5977 #endif
5978       else
5979         Store[x][y] = EL_EMPTY;
5980
5981       if (IS_CUSTOM_ELEMENT(center_element))
5982         Store[x][y] = (Store[x][y] == EL_CURRENT_CE_VALUE ? ce_value :
5983                        Store[x][y] == EL_CURRENT_CE_SCORE ? ce_score :
5984                        Store[x][y] >= EL_PREV_CE_8 &&
5985                        Store[x][y] <= EL_NEXT_CE_8 ?
5986                        RESOLVED_REFERENCE_ELEMENT(center_element, Store[x][y]) :
5987                        Store[x][y]);
5988
5989       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5990           center_element == EL_AMOEBA_TO_DIAMOND)
5991         Store2[x][y] = element;
5992
5993       Tile[x][y] = EL_EXPLOSION;
5994       GfxElement[x][y] = artwork_element;
5995
5996       ExplodePhase[x][y] = 1;
5997       ExplodeDelay[x][y] = last_phase;
5998
5999       Stop[x][y] = TRUE;
6000     }
6001
6002     if (center_element == EL_YAMYAM)
6003       game.yamyam_content_nr =
6004         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6005
6006     return;
6007   }
6008
6009   if (Stop[ex][ey])
6010     return;
6011
6012   x = ex;
6013   y = ey;
6014
6015   if (phase == 1)
6016     GfxFrame[x][y] = 0;         // restart explosion animation
6017
6018   last_phase = ExplodeDelay[x][y];
6019
6020   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6021
6022   // this can happen if the player leaves an explosion just in time
6023   if (GfxElement[x][y] == EL_UNDEFINED)
6024     GfxElement[x][y] = EL_EMPTY;
6025
6026   border_element = Store2[x][y];
6027   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6028     border_element = StorePlayer[x][y];
6029
6030   if (phase == element_info[border_element].ignition_delay ||
6031       phase == last_phase)
6032   {
6033     boolean border_explosion = FALSE;
6034
6035     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6036         !PLAYER_EXPLOSION_PROTECTED(x, y))
6037     {
6038       KillPlayerUnlessExplosionProtected(x, y);
6039       border_explosion = TRUE;
6040     }
6041     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6042     {
6043       Tile[x][y] = Store2[x][y];
6044       Store2[x][y] = 0;
6045       Bang(x, y);
6046       border_explosion = TRUE;
6047     }
6048     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6049     {
6050       AmoebaToDiamond(x, y);
6051       Store2[x][y] = 0;
6052       border_explosion = TRUE;
6053     }
6054
6055     // if an element just explodes due to another explosion (chain-reaction),
6056     // do not immediately end the new explosion when it was the last frame of
6057     // the explosion (as it would be done in the following "if"-statement!)
6058     if (border_explosion && phase == last_phase)
6059       return;
6060   }
6061
6062   // this can happen if the player was just killed by an explosion
6063   if (GfxElement[x][y] == EL_UNDEFINED)
6064     GfxElement[x][y] = EL_EMPTY;
6065
6066   if (phase == last_phase)
6067   {
6068     int element;
6069
6070     element = Tile[x][y] = Store[x][y];
6071     Store[x][y] = Store2[x][y] = 0;
6072     GfxElement[x][y] = EL_UNDEFINED;
6073
6074     // player can escape from explosions and might therefore be still alive
6075     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6076         element <= EL_PLAYER_IS_EXPLODING_4)
6077     {
6078       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6079       int explosion_element = EL_PLAYER_1 + player_nr;
6080       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6081       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6082
6083       if (level.use_explosion_element[player_nr])
6084         explosion_element = level.explosion_element[player_nr];
6085
6086       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6087                     element_info[explosion_element].content.e[xx][yy]);
6088     }
6089
6090     // restore probably existing indestructible background element
6091     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6092       element = Tile[x][y] = Back[x][y];
6093     Back[x][y] = 0;
6094
6095     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6096     GfxDir[x][y] = MV_NONE;
6097     ChangeDelay[x][y] = 0;
6098     ChangePage[x][y] = -1;
6099
6100     CustomValue[x][y] = 0;
6101
6102     InitField_WithBug2(x, y, FALSE);
6103
6104     TEST_DrawLevelField(x, y);
6105
6106     TestIfElementTouchesCustomElement(x, y);
6107
6108     if (GFX_CRUMBLED(element))
6109       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6110
6111     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6112       StorePlayer[x][y] = 0;
6113
6114     if (IS_PLAYER_ELEMENT(element))
6115       RelocatePlayer(x, y, element);
6116   }
6117   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6118   {
6119     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6120     int frame = getGraphicAnimationFrameXY(graphic, x, y);
6121
6122     if (phase == 1)
6123       TEST_DrawLevelFieldCrumbled(x, y);
6124
6125     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6126     {
6127       DrawLevelElement(x, y, Back[x][y]);
6128       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6129     }
6130     else if (IS_WALKABLE_UNDER(Back[x][y]))
6131     {
6132       DrawLevelGraphic(x, y, graphic, frame);
6133       DrawLevelElementThruMask(x, y, Back[x][y]);
6134     }
6135     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6136       DrawLevelGraphic(x, y, graphic, frame);
6137   }
6138 }
6139
6140 static void DynaExplode(int ex, int ey)
6141 {
6142   int i, j;
6143   int dynabomb_element = Tile[ex][ey];
6144   int dynabomb_size = 1;
6145   boolean dynabomb_xl = FALSE;
6146   struct PlayerInfo *player;
6147   struct XY *xy = xy_topdown;
6148
6149   if (IS_ACTIVE_BOMB(dynabomb_element))
6150   {
6151     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6152     dynabomb_size = player->dynabomb_size;
6153     dynabomb_xl = player->dynabomb_xl;
6154     player->dynabombs_left++;
6155   }
6156
6157   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6158
6159   for (i = 0; i < NUM_DIRECTIONS; i++)
6160   {
6161     for (j = 1; j <= dynabomb_size; j++)
6162     {
6163       int x = ex + j * xy[i].x;
6164       int y = ey + j * xy[i].y;
6165       int element;
6166
6167       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6168         break;
6169
6170       element = Tile[x][y];
6171
6172       // do not restart explosions of fields with active bombs
6173       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6174         continue;
6175
6176       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6177
6178       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6179           !IS_DIGGABLE(element) && !dynabomb_xl)
6180         break;
6181     }
6182   }
6183 }
6184
6185 void Bang(int x, int y)
6186 {
6187   int element = MovingOrBlocked2Element(x, y);
6188   int explosion_type = EX_TYPE_NORMAL;
6189
6190   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6191   {
6192     struct PlayerInfo *player = PLAYERINFO(x, y);
6193
6194     element = Tile[x][y] = player->initial_element;
6195
6196     if (level.use_explosion_element[player->index_nr])
6197     {
6198       int explosion_element = level.explosion_element[player->index_nr];
6199
6200       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6201         explosion_type = EX_TYPE_CROSS;
6202       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6203         explosion_type = EX_TYPE_CENTER;
6204     }
6205   }
6206
6207   switch (element)
6208   {
6209     case EL_BUG:
6210     case EL_SPACESHIP:
6211     case EL_BD_BUTTERFLY:
6212     case EL_BD_FIREFLY:
6213     case EL_YAMYAM:
6214     case EL_DARK_YAMYAM:
6215     case EL_ROBOT:
6216     case EL_PACMAN:
6217     case EL_MOLE:
6218       RaiseScoreElement(element);
6219       break;
6220
6221     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6222     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6223     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6224     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6225     case EL_DYNABOMB_INCREASE_NUMBER:
6226     case EL_DYNABOMB_INCREASE_SIZE:
6227     case EL_DYNABOMB_INCREASE_POWER:
6228       explosion_type = EX_TYPE_DYNA;
6229       break;
6230
6231     case EL_DC_LANDMINE:
6232       explosion_type = EX_TYPE_CENTER;
6233       break;
6234
6235     case EL_PENGUIN:
6236     case EL_LAMP:
6237     case EL_LAMP_ACTIVE:
6238     case EL_AMOEBA_TO_DIAMOND:
6239       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6240         explosion_type = EX_TYPE_CENTER;
6241       break;
6242
6243     default:
6244       if (element_info[element].explosion_type == EXPLODES_CROSS)
6245         explosion_type = EX_TYPE_CROSS;
6246       else if (element_info[element].explosion_type == EXPLODES_1X1)
6247         explosion_type = EX_TYPE_CENTER;
6248       break;
6249   }
6250
6251   if (explosion_type == EX_TYPE_DYNA)
6252     DynaExplode(x, y);
6253   else
6254     Explode(x, y, EX_PHASE_START, explosion_type);
6255
6256   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6257 }
6258
6259 static void SplashAcid(int x, int y)
6260 {
6261   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6262       (!IN_LEV_FIELD(x - 1, y - 2) ||
6263        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6264     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6265
6266   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6267       (!IN_LEV_FIELD(x + 1, y - 2) ||
6268        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6269     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6270
6271   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6272 }
6273
6274 static void InitBeltMovement(void)
6275 {
6276   static int belt_base_element[4] =
6277   {
6278     EL_CONVEYOR_BELT_1_LEFT,
6279     EL_CONVEYOR_BELT_2_LEFT,
6280     EL_CONVEYOR_BELT_3_LEFT,
6281     EL_CONVEYOR_BELT_4_LEFT
6282   };
6283   static int belt_base_active_element[4] =
6284   {
6285     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6286     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6287     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6288     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6289   };
6290
6291   int x, y, i, j;
6292
6293   // set frame order for belt animation graphic according to belt direction
6294   for (i = 0; i < NUM_BELTS; i++)
6295   {
6296     int belt_nr = i;
6297
6298     for (j = 0; j < NUM_BELT_PARTS; j++)
6299     {
6300       int element = belt_base_active_element[belt_nr] + j;
6301       int graphic_1 = el2img(element);
6302       int graphic_2 = el2panelimg(element);
6303
6304       if (game.belt_dir[i] == MV_LEFT)
6305       {
6306         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6307         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6308       }
6309       else
6310       {
6311         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6312         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6313       }
6314     }
6315   }
6316
6317   SCAN_PLAYFIELD(x, y)
6318   {
6319     int element = Tile[x][y];
6320
6321     for (i = 0; i < NUM_BELTS; i++)
6322     {
6323       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6324       {
6325         int e_belt_nr = getBeltNrFromBeltElement(element);
6326         int belt_nr = i;
6327
6328         if (e_belt_nr == belt_nr)
6329         {
6330           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6331
6332           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6333         }
6334       }
6335     }
6336   }
6337 }
6338
6339 static void ToggleBeltSwitch(int x, int y)
6340 {
6341   static int belt_base_element[4] =
6342   {
6343     EL_CONVEYOR_BELT_1_LEFT,
6344     EL_CONVEYOR_BELT_2_LEFT,
6345     EL_CONVEYOR_BELT_3_LEFT,
6346     EL_CONVEYOR_BELT_4_LEFT
6347   };
6348   static int belt_base_active_element[4] =
6349   {
6350     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6351     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6352     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6353     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6354   };
6355   static int belt_base_switch_element[4] =
6356   {
6357     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6358     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6359     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6360     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6361   };
6362   static int belt_move_dir[4] =
6363   {
6364     MV_LEFT,
6365     MV_NONE,
6366     MV_RIGHT,
6367     MV_NONE,
6368   };
6369
6370   int element = Tile[x][y];
6371   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6372   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6373   int belt_dir = belt_move_dir[belt_dir_nr];
6374   int xx, yy, i;
6375
6376   if (!IS_BELT_SWITCH(element))
6377     return;
6378
6379   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6380   game.belt_dir[belt_nr] = belt_dir;
6381
6382   if (belt_dir_nr == 3)
6383     belt_dir_nr = 1;
6384
6385   // set frame order for belt animation graphic according to belt direction
6386   for (i = 0; i < NUM_BELT_PARTS; i++)
6387   {
6388     int element = belt_base_active_element[belt_nr] + i;
6389     int graphic_1 = el2img(element);
6390     int graphic_2 = el2panelimg(element);
6391
6392     if (belt_dir == MV_LEFT)
6393     {
6394       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6395       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6396     }
6397     else
6398     {
6399       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6400       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6401     }
6402   }
6403
6404   SCAN_PLAYFIELD(xx, yy)
6405   {
6406     int element = Tile[xx][yy];
6407
6408     if (IS_BELT_SWITCH(element))
6409     {
6410       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6411
6412       if (e_belt_nr == belt_nr)
6413       {
6414         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6415         TEST_DrawLevelField(xx, yy);
6416       }
6417     }
6418     else if (IS_BELT(element) && belt_dir != MV_NONE)
6419     {
6420       int e_belt_nr = getBeltNrFromBeltElement(element);
6421
6422       if (e_belt_nr == belt_nr)
6423       {
6424         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6425
6426         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6427         TEST_DrawLevelField(xx, yy);
6428       }
6429     }
6430     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6431     {
6432       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6433
6434       if (e_belt_nr == belt_nr)
6435       {
6436         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6437
6438         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6439         TEST_DrawLevelField(xx, yy);
6440       }
6441     }
6442   }
6443 }
6444
6445 static void ToggleSwitchgateSwitch(void)
6446 {
6447   int xx, yy;
6448
6449   game.switchgate_pos = !game.switchgate_pos;
6450
6451   SCAN_PLAYFIELD(xx, yy)
6452   {
6453     int element = Tile[xx][yy];
6454
6455     if (element == EL_SWITCHGATE_SWITCH_UP)
6456     {
6457       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6458       TEST_DrawLevelField(xx, yy);
6459     }
6460     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6461     {
6462       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6463       TEST_DrawLevelField(xx, yy);
6464     }
6465     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6466     {
6467       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6468       TEST_DrawLevelField(xx, yy);
6469     }
6470     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6471     {
6472       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6473       TEST_DrawLevelField(xx, yy);
6474     }
6475     else if (element == EL_SWITCHGATE_OPEN ||
6476              element == EL_SWITCHGATE_OPENING)
6477     {
6478       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6479
6480       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6481     }
6482     else if (element == EL_SWITCHGATE_CLOSED ||
6483              element == EL_SWITCHGATE_CLOSING)
6484     {
6485       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6486
6487       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6488     }
6489   }
6490 }
6491
6492 static int getInvisibleActiveFromInvisibleElement(int element)
6493 {
6494   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6495           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6496           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6497           element);
6498 }
6499
6500 static int getInvisibleFromInvisibleActiveElement(int element)
6501 {
6502   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6503           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6504           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6505           element);
6506 }
6507
6508 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6509 {
6510   int x, y;
6511
6512   SCAN_PLAYFIELD(x, y)
6513   {
6514     int element = Tile[x][y];
6515
6516     if (element == EL_LIGHT_SWITCH &&
6517         game.light_time_left > 0)
6518     {
6519       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6520       TEST_DrawLevelField(x, y);
6521     }
6522     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6523              game.light_time_left == 0)
6524     {
6525       Tile[x][y] = EL_LIGHT_SWITCH;
6526       TEST_DrawLevelField(x, y);
6527     }
6528     else if (element == EL_EMC_DRIPPER &&
6529              game.light_time_left > 0)
6530     {
6531       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6532       TEST_DrawLevelField(x, y);
6533     }
6534     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6535              game.light_time_left == 0)
6536     {
6537       Tile[x][y] = EL_EMC_DRIPPER;
6538       TEST_DrawLevelField(x, y);
6539     }
6540     else if (element == EL_INVISIBLE_STEELWALL ||
6541              element == EL_INVISIBLE_WALL ||
6542              element == EL_INVISIBLE_SAND)
6543     {
6544       if (game.light_time_left > 0)
6545         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6546
6547       TEST_DrawLevelField(x, y);
6548
6549       // uncrumble neighbour fields, if needed
6550       if (element == EL_INVISIBLE_SAND)
6551         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6552     }
6553     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6554              element == EL_INVISIBLE_WALL_ACTIVE ||
6555              element == EL_INVISIBLE_SAND_ACTIVE)
6556     {
6557       if (game.light_time_left == 0)
6558         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6559
6560       TEST_DrawLevelField(x, y);
6561
6562       // re-crumble neighbour fields, if needed
6563       if (element == EL_INVISIBLE_SAND)
6564         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6565     }
6566   }
6567 }
6568
6569 static void RedrawAllInvisibleElementsForLenses(void)
6570 {
6571   int x, y;
6572
6573   SCAN_PLAYFIELD(x, y)
6574   {
6575     int element = Tile[x][y];
6576
6577     if (element == EL_EMC_DRIPPER &&
6578         game.lenses_time_left > 0)
6579     {
6580       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6581       TEST_DrawLevelField(x, y);
6582     }
6583     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6584              game.lenses_time_left == 0)
6585     {
6586       Tile[x][y] = EL_EMC_DRIPPER;
6587       TEST_DrawLevelField(x, y);
6588     }
6589     else if (element == EL_INVISIBLE_STEELWALL ||
6590              element == EL_INVISIBLE_WALL ||
6591              element == EL_INVISIBLE_SAND)
6592     {
6593       if (game.lenses_time_left > 0)
6594         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6595
6596       TEST_DrawLevelField(x, y);
6597
6598       // uncrumble neighbour fields, if needed
6599       if (element == EL_INVISIBLE_SAND)
6600         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6601     }
6602     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6603              element == EL_INVISIBLE_WALL_ACTIVE ||
6604              element == EL_INVISIBLE_SAND_ACTIVE)
6605     {
6606       if (game.lenses_time_left == 0)
6607         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6608
6609       TEST_DrawLevelField(x, y);
6610
6611       // re-crumble neighbour fields, if needed
6612       if (element == EL_INVISIBLE_SAND)
6613         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6614     }
6615   }
6616 }
6617
6618 static void RedrawAllInvisibleElementsForMagnifier(void)
6619 {
6620   int x, y;
6621
6622   SCAN_PLAYFIELD(x, y)
6623   {
6624     int element = Tile[x][y];
6625
6626     if (element == EL_EMC_FAKE_GRASS &&
6627         game.magnify_time_left > 0)
6628     {
6629       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6630       TEST_DrawLevelField(x, y);
6631     }
6632     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6633              game.magnify_time_left == 0)
6634     {
6635       Tile[x][y] = EL_EMC_FAKE_GRASS;
6636       TEST_DrawLevelField(x, y);
6637     }
6638     else if (IS_GATE_GRAY(element) &&
6639              game.magnify_time_left > 0)
6640     {
6641       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6642                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6643                     IS_EM_GATE_GRAY(element) ?
6644                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6645                     IS_EMC_GATE_GRAY(element) ?
6646                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6647                     IS_DC_GATE_GRAY(element) ?
6648                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6649                     element);
6650       TEST_DrawLevelField(x, y);
6651     }
6652     else if (IS_GATE_GRAY_ACTIVE(element) &&
6653              game.magnify_time_left == 0)
6654     {
6655       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6656                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6657                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6658                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6659                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6660                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6661                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6662                     EL_DC_GATE_WHITE_GRAY :
6663                     element);
6664       TEST_DrawLevelField(x, y);
6665     }
6666   }
6667 }
6668
6669 static void ToggleLightSwitch(int x, int y)
6670 {
6671   int element = Tile[x][y];
6672
6673   game.light_time_left =
6674     (element == EL_LIGHT_SWITCH ?
6675      level.time_light * FRAMES_PER_SECOND : 0);
6676
6677   RedrawAllLightSwitchesAndInvisibleElements();
6678 }
6679
6680 static void ActivateTimegateSwitch(int x, int y)
6681 {
6682   int xx, yy;
6683
6684   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6685
6686   SCAN_PLAYFIELD(xx, yy)
6687   {
6688     int element = Tile[xx][yy];
6689
6690     if (element == EL_TIMEGATE_CLOSED ||
6691         element == EL_TIMEGATE_CLOSING)
6692     {
6693       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6694       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6695     }
6696
6697     /*
6698     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6699     {
6700       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6701       TEST_DrawLevelField(xx, yy);
6702     }
6703     */
6704
6705   }
6706
6707   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6708                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6709 }
6710
6711 static void Impact(int x, int y)
6712 {
6713   boolean last_line = (y == lev_fieldy - 1);
6714   boolean object_hit = FALSE;
6715   boolean impact = (last_line || object_hit);
6716   int element = Tile[x][y];
6717   int smashed = EL_STEELWALL;
6718
6719   if (!last_line)       // check if element below was hit
6720   {
6721     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6722       return;
6723
6724     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6725                                          MovDir[x][y + 1] != MV_DOWN ||
6726                                          MovPos[x][y + 1] <= TILEY / 2));
6727
6728     // do not smash moving elements that left the smashed field in time
6729     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6730         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6731       object_hit = FALSE;
6732
6733 #if USE_QUICKSAND_IMPACT_BUGFIX
6734     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6735     {
6736       RemoveMovingField(x, y + 1);
6737       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6738       Tile[x][y + 2] = EL_ROCK;
6739       TEST_DrawLevelField(x, y + 2);
6740
6741       object_hit = TRUE;
6742     }
6743
6744     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6745     {
6746       RemoveMovingField(x, y + 1);
6747       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6748       Tile[x][y + 2] = EL_ROCK;
6749       TEST_DrawLevelField(x, y + 2);
6750
6751       object_hit = TRUE;
6752     }
6753 #endif
6754
6755     if (object_hit)
6756       smashed = MovingOrBlocked2Element(x, y + 1);
6757
6758     impact = (last_line || object_hit);
6759   }
6760
6761   if (!last_line && smashed == EL_ACID) // element falls into acid
6762   {
6763     SplashAcid(x, y + 1);
6764     return;
6765   }
6766
6767   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6768   // only reset graphic animation if graphic really changes after impact
6769   if (impact &&
6770       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6771   {
6772     ResetGfxAnimation(x, y);
6773     TEST_DrawLevelField(x, y);
6774   }
6775
6776   if (impact && CAN_EXPLODE_IMPACT(element))
6777   {
6778     Bang(x, y);
6779     return;
6780   }
6781   else if (impact && element == EL_PEARL &&
6782            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6783   {
6784     ResetGfxAnimation(x, y);
6785
6786     Tile[x][y] = EL_PEARL_BREAKING;
6787     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6788     return;
6789   }
6790   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6791   {
6792     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6793
6794     return;
6795   }
6796
6797   if (impact && element == EL_AMOEBA_DROP)
6798   {
6799     if (object_hit && IS_PLAYER(x, y + 1))
6800       KillPlayerUnlessEnemyProtected(x, y + 1);
6801     else if (object_hit && smashed == EL_PENGUIN)
6802       Bang(x, y + 1);
6803     else
6804     {
6805       Tile[x][y] = EL_AMOEBA_GROWING;
6806       Store[x][y] = EL_AMOEBA_WET;
6807
6808       ResetRandomAnimationValue(x, y);
6809     }
6810     return;
6811   }
6812
6813   if (object_hit)               // check which object was hit
6814   {
6815     if ((CAN_PASS_MAGIC_WALL(element) && 
6816          (smashed == EL_MAGIC_WALL ||
6817           smashed == EL_BD_MAGIC_WALL)) ||
6818         (CAN_PASS_DC_MAGIC_WALL(element) &&
6819          smashed == EL_DC_MAGIC_WALL))
6820     {
6821       int xx, yy;
6822       int activated_magic_wall =
6823         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6824          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6825          EL_DC_MAGIC_WALL_ACTIVE);
6826
6827       // activate magic wall / mill
6828       SCAN_PLAYFIELD(xx, yy)
6829       {
6830         if (Tile[xx][yy] == smashed)
6831           Tile[xx][yy] = activated_magic_wall;
6832       }
6833
6834       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6835       game.magic_wall_active = TRUE;
6836
6837       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6838                             SND_MAGIC_WALL_ACTIVATING :
6839                             smashed == EL_BD_MAGIC_WALL ?
6840                             SND_BD_MAGIC_WALL_ACTIVATING :
6841                             SND_DC_MAGIC_WALL_ACTIVATING));
6842     }
6843
6844     if (IS_PLAYER(x, y + 1))
6845     {
6846       if (CAN_SMASH_PLAYER(element))
6847       {
6848         KillPlayerUnlessEnemyProtected(x, y + 1);
6849         return;
6850       }
6851     }
6852     else if (smashed == EL_PENGUIN)
6853     {
6854       if (CAN_SMASH_PLAYER(element))
6855       {
6856         Bang(x, y + 1);
6857         return;
6858       }
6859     }
6860     else if (element == EL_BD_DIAMOND)
6861     {
6862       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6863       {
6864         Bang(x, y + 1);
6865         return;
6866       }
6867     }
6868     else if (((element == EL_SP_INFOTRON ||
6869                element == EL_SP_ZONK) &&
6870               (smashed == EL_SP_SNIKSNAK ||
6871                smashed == EL_SP_ELECTRON ||
6872                smashed == EL_SP_DISK_ORANGE)) ||
6873              (element == EL_SP_INFOTRON &&
6874               smashed == EL_SP_DISK_YELLOW))
6875     {
6876       Bang(x, y + 1);
6877       return;
6878     }
6879     else if (CAN_SMASH_EVERYTHING(element))
6880     {
6881       if (IS_CLASSIC_ENEMY(smashed) ||
6882           CAN_EXPLODE_SMASHED(smashed))
6883       {
6884         Bang(x, y + 1);
6885         return;
6886       }
6887       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6888       {
6889         if (smashed == EL_LAMP ||
6890             smashed == EL_LAMP_ACTIVE)
6891         {
6892           Bang(x, y + 1);
6893           return;
6894         }
6895         else if (smashed == EL_NUT)
6896         {
6897           Tile[x][y + 1] = EL_NUT_BREAKING;
6898           PlayLevelSound(x, y, SND_NUT_BREAKING);
6899           RaiseScoreElement(EL_NUT);
6900           return;
6901         }
6902         else if (smashed == EL_PEARL)
6903         {
6904           ResetGfxAnimation(x, y);
6905
6906           Tile[x][y + 1] = EL_PEARL_BREAKING;
6907           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6908           return;
6909         }
6910         else if (smashed == EL_DIAMOND)
6911         {
6912           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6913           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6914           return;
6915         }
6916         else if (IS_BELT_SWITCH(smashed))
6917         {
6918           ToggleBeltSwitch(x, y + 1);
6919         }
6920         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6921                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6922                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6923                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6924         {
6925           ToggleSwitchgateSwitch();
6926         }
6927         else if (smashed == EL_LIGHT_SWITCH ||
6928                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6929         {
6930           ToggleLightSwitch(x, y + 1);
6931         }
6932         else
6933         {
6934           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6935
6936           CheckElementChangeBySide(x, y + 1, smashed, element,
6937                                    CE_SWITCHED, CH_SIDE_TOP);
6938           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6939                                             CH_SIDE_TOP);
6940         }
6941       }
6942       else
6943       {
6944         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6945       }
6946     }
6947   }
6948
6949   // play sound of magic wall / mill
6950   if (!last_line &&
6951       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6952        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6953        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6954   {
6955     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6956       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6957     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6958       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6959     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6960       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6961
6962     return;
6963   }
6964
6965   // play sound of object that hits the ground
6966   if (last_line || object_hit)
6967     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6968 }
6969
6970 static void TurnRoundExt(int x, int y)
6971 {
6972   static struct
6973   {
6974     int dx, dy;
6975   } move_xy[] =
6976   {
6977     {  0,  0 },
6978     { -1,  0 },
6979     { +1,  0 },
6980     {  0,  0 },
6981     {  0, -1 },
6982     {  0,  0 }, { 0, 0 }, { 0, 0 },
6983     {  0, +1 }
6984   };
6985   static struct
6986   {
6987     int left, right, back;
6988   } turn[] =
6989   {
6990     { 0,        0,              0        },
6991     { MV_DOWN,  MV_UP,          MV_RIGHT },
6992     { MV_UP,    MV_DOWN,        MV_LEFT  },
6993     { 0,        0,              0        },
6994     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6995     { 0,        0,              0        },
6996     { 0,        0,              0        },
6997     { 0,        0,              0        },
6998     { MV_RIGHT, MV_LEFT,        MV_UP    }
6999   };
7000
7001   int element = Tile[x][y];
7002   int move_pattern = element_info[element].move_pattern;
7003
7004   int old_move_dir = MovDir[x][y];
7005   int left_dir  = turn[old_move_dir].left;
7006   int right_dir = turn[old_move_dir].right;
7007   int back_dir  = turn[old_move_dir].back;
7008
7009   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7010   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7011   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7012   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7013
7014   int left_x  = x + left_dx,  left_y  = y + left_dy;
7015   int right_x = x + right_dx, right_y = y + right_dy;
7016   int move_x  = x + move_dx,  move_y  = y + move_dy;
7017
7018   int xx, yy;
7019
7020   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7021   {
7022     TestIfBadThingTouchesOtherBadThing(x, y);
7023
7024     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7025       MovDir[x][y] = right_dir;
7026     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7027       MovDir[x][y] = left_dir;
7028
7029     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7030       MovDelay[x][y] = 9;
7031     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
7032       MovDelay[x][y] = 1;
7033   }
7034   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7035   {
7036     TestIfBadThingTouchesOtherBadThing(x, y);
7037
7038     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7039       MovDir[x][y] = left_dir;
7040     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7041       MovDir[x][y] = right_dir;
7042
7043     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7044       MovDelay[x][y] = 9;
7045     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
7046       MovDelay[x][y] = 1;
7047   }
7048   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7049   {
7050     TestIfBadThingTouchesOtherBadThing(x, y);
7051
7052     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7053       MovDir[x][y] = left_dir;
7054     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7055       MovDir[x][y] = right_dir;
7056
7057     if (MovDir[x][y] != old_move_dir)
7058       MovDelay[x][y] = 9;
7059   }
7060   else if (element == EL_YAMYAM)
7061   {
7062     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7063     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7064
7065     if (can_turn_left && can_turn_right)
7066       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7067     else if (can_turn_left)
7068       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7069     else if (can_turn_right)
7070       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7071     else
7072       MovDir[x][y] = back_dir;
7073
7074     MovDelay[x][y] = 16 + 16 * RND(3);
7075   }
7076   else if (element == EL_DARK_YAMYAM)
7077   {
7078     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7079                                                          left_x, left_y);
7080     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7081                                                          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_PACMAN)
7095   {
7096     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7097     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7098
7099     if (can_turn_left && can_turn_right)
7100       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7101     else if (can_turn_left)
7102       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7103     else if (can_turn_right)
7104       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7105     else
7106       MovDir[x][y] = back_dir;
7107
7108     MovDelay[x][y] = 6 + RND(40);
7109   }
7110   else if (element == EL_PIG)
7111   {
7112     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7113     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7114     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7115     boolean should_turn_left, should_turn_right, should_move_on;
7116     int rnd_value = 24;
7117     int rnd = RND(rnd_value);
7118
7119     should_turn_left = (can_turn_left &&
7120                         (!can_move_on ||
7121                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7122                                                    y + back_dy + left_dy)));
7123     should_turn_right = (can_turn_right &&
7124                          (!can_move_on ||
7125                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7126                                                     y + back_dy + right_dy)));
7127     should_move_on = (can_move_on &&
7128                       (!can_turn_left ||
7129                        !can_turn_right ||
7130                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7131                                                  y + move_dy + left_dy) ||
7132                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7133                                                  y + move_dy + right_dy)));
7134
7135     if (should_turn_left || should_turn_right || should_move_on)
7136     {
7137       if (should_turn_left && should_turn_right && should_move_on)
7138         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7139                         rnd < 2 * rnd_value / 3 ? right_dir :
7140                         old_move_dir);
7141       else if (should_turn_left && should_turn_right)
7142         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7143       else if (should_turn_left && should_move_on)
7144         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7145       else if (should_turn_right && should_move_on)
7146         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7147       else if (should_turn_left)
7148         MovDir[x][y] = left_dir;
7149       else if (should_turn_right)
7150         MovDir[x][y] = right_dir;
7151       else if (should_move_on)
7152         MovDir[x][y] = old_move_dir;
7153     }
7154     else if (can_move_on && rnd > rnd_value / 8)
7155       MovDir[x][y] = old_move_dir;
7156     else if (can_turn_left && can_turn_right)
7157       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7158     else if (can_turn_left && rnd > rnd_value / 8)
7159       MovDir[x][y] = left_dir;
7160     else if (can_turn_right && rnd > rnd_value/8)
7161       MovDir[x][y] = right_dir;
7162     else
7163       MovDir[x][y] = back_dir;
7164
7165     xx = x + move_xy[MovDir[x][y]].dx;
7166     yy = y + move_xy[MovDir[x][y]].dy;
7167
7168     if (!IN_LEV_FIELD(xx, yy) ||
7169         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7170       MovDir[x][y] = old_move_dir;
7171
7172     MovDelay[x][y] = 0;
7173   }
7174   else if (element == EL_DRAGON)
7175   {
7176     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7177     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7178     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7179     int rnd_value = 24;
7180     int rnd = RND(rnd_value);
7181
7182     if (can_move_on && rnd > rnd_value / 8)
7183       MovDir[x][y] = old_move_dir;
7184     else if (can_turn_left && can_turn_right)
7185       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7186     else if (can_turn_left && rnd > rnd_value / 8)
7187       MovDir[x][y] = left_dir;
7188     else if (can_turn_right && rnd > rnd_value / 8)
7189       MovDir[x][y] = right_dir;
7190     else
7191       MovDir[x][y] = back_dir;
7192
7193     xx = x + move_xy[MovDir[x][y]].dx;
7194     yy = y + move_xy[MovDir[x][y]].dy;
7195
7196     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7197       MovDir[x][y] = old_move_dir;
7198
7199     MovDelay[x][y] = 0;
7200   }
7201   else if (element == EL_MOLE)
7202   {
7203     boolean can_move_on =
7204       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7205                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7206                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7207     if (!can_move_on)
7208     {
7209       boolean can_turn_left =
7210         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7211                               IS_AMOEBOID(Tile[left_x][left_y])));
7212
7213       boolean can_turn_right =
7214         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7215                               IS_AMOEBOID(Tile[right_x][right_y])));
7216
7217       if (can_turn_left && can_turn_right)
7218         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7219       else if (can_turn_left)
7220         MovDir[x][y] = left_dir;
7221       else
7222         MovDir[x][y] = right_dir;
7223     }
7224
7225     if (MovDir[x][y] != old_move_dir)
7226       MovDelay[x][y] = 9;
7227   }
7228   else if (element == EL_BALLOON)
7229   {
7230     MovDir[x][y] = game.wind_direction;
7231     MovDelay[x][y] = 0;
7232   }
7233   else if (element == EL_SPRING)
7234   {
7235     if (MovDir[x][y] & MV_HORIZONTAL)
7236     {
7237       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7238           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7239       {
7240         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7241         ResetGfxAnimation(move_x, move_y);
7242         TEST_DrawLevelField(move_x, move_y);
7243
7244         MovDir[x][y] = back_dir;
7245       }
7246       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7247                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7248         MovDir[x][y] = MV_NONE;
7249     }
7250
7251     MovDelay[x][y] = 0;
7252   }
7253   else if (element == EL_ROBOT ||
7254            element == EL_SATELLITE ||
7255            element == EL_PENGUIN ||
7256            element == EL_EMC_ANDROID)
7257   {
7258     int attr_x = -1, attr_y = -1;
7259
7260     if (game.all_players_gone)
7261     {
7262       attr_x = game.exit_x;
7263       attr_y = game.exit_y;
7264     }
7265     else
7266     {
7267       int i;
7268
7269       for (i = 0; i < MAX_PLAYERS; i++)
7270       {
7271         struct PlayerInfo *player = &stored_player[i];
7272         int jx = player->jx, jy = player->jy;
7273
7274         if (!player->active)
7275           continue;
7276
7277         if (attr_x == -1 ||
7278             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7279         {
7280           attr_x = jx;
7281           attr_y = jy;
7282         }
7283       }
7284     }
7285
7286     if (element == EL_ROBOT &&
7287         game.robot_wheel_x >= 0 &&
7288         game.robot_wheel_y >= 0 &&
7289         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7290          game.engine_version < VERSION_IDENT(3,1,0,0)))
7291     {
7292       attr_x = game.robot_wheel_x;
7293       attr_y = game.robot_wheel_y;
7294     }
7295
7296     if (element == EL_PENGUIN)
7297     {
7298       int i;
7299       struct XY *xy = xy_topdown;
7300
7301       for (i = 0; i < NUM_DIRECTIONS; i++)
7302       {
7303         int ex = x + xy[i].x;
7304         int ey = y + xy[i].y;
7305
7306         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7307                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7308                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7309                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7310         {
7311           attr_x = ex;
7312           attr_y = ey;
7313           break;
7314         }
7315       }
7316     }
7317
7318     MovDir[x][y] = MV_NONE;
7319     if (attr_x < x)
7320       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7321     else if (attr_x > x)
7322       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7323     if (attr_y < y)
7324       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7325     else if (attr_y > y)
7326       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7327
7328     if (element == EL_ROBOT)
7329     {
7330       int newx, newy;
7331
7332       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7333         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7334       Moving2Blocked(x, y, &newx, &newy);
7335
7336       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7337         MovDelay[x][y] = 8 + 8 * !RND(3);
7338       else
7339         MovDelay[x][y] = 16;
7340     }
7341     else if (element == EL_PENGUIN)
7342     {
7343       int newx, newy;
7344
7345       MovDelay[x][y] = 1;
7346
7347       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7348       {
7349         boolean first_horiz = RND(2);
7350         int new_move_dir = MovDir[x][y];
7351
7352         MovDir[x][y] =
7353           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7354         Moving2Blocked(x, y, &newx, &newy);
7355
7356         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7357           return;
7358
7359         MovDir[x][y] =
7360           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7361         Moving2Blocked(x, y, &newx, &newy);
7362
7363         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7364           return;
7365
7366         MovDir[x][y] = old_move_dir;
7367         return;
7368       }
7369     }
7370     else if (element == EL_SATELLITE)
7371     {
7372       int newx, newy;
7373
7374       MovDelay[x][y] = 1;
7375
7376       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7377       {
7378         boolean first_horiz = RND(2);
7379         int new_move_dir = MovDir[x][y];
7380
7381         MovDir[x][y] =
7382           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7383         Moving2Blocked(x, y, &newx, &newy);
7384
7385         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7386           return;
7387
7388         MovDir[x][y] =
7389           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7390         Moving2Blocked(x, y, &newx, &newy);
7391
7392         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7393           return;
7394
7395         MovDir[x][y] = old_move_dir;
7396         return;
7397       }
7398     }
7399     else if (element == EL_EMC_ANDROID)
7400     {
7401       static int check_pos[16] =
7402       {
7403         -1,             //  0 => (invalid)
7404         7,              //  1 => MV_LEFT
7405         3,              //  2 => MV_RIGHT
7406         -1,             //  3 => (invalid)
7407         1,              //  4 =>            MV_UP
7408         0,              //  5 => MV_LEFT  | MV_UP
7409         2,              //  6 => MV_RIGHT | MV_UP
7410         -1,             //  7 => (invalid)
7411         5,              //  8 =>            MV_DOWN
7412         6,              //  9 => MV_LEFT  | MV_DOWN
7413         4,              // 10 => MV_RIGHT | MV_DOWN
7414         -1,             // 11 => (invalid)
7415         -1,             // 12 => (invalid)
7416         -1,             // 13 => (invalid)
7417         -1,             // 14 => (invalid)
7418         -1,             // 15 => (invalid)
7419       };
7420       static struct
7421       {
7422         int dx, dy;
7423         int dir;
7424       } check_xy[8] =
7425       {
7426         { -1, -1,       MV_LEFT  | MV_UP   },
7427         {  0, -1,                  MV_UP   },
7428         { +1, -1,       MV_RIGHT | MV_UP   },
7429         { +1,  0,       MV_RIGHT           },
7430         { +1, +1,       MV_RIGHT | MV_DOWN },
7431         {  0, +1,                  MV_DOWN },
7432         { -1, +1,       MV_LEFT  | MV_DOWN },
7433         { -1,  0,       MV_LEFT            },
7434       };
7435       int start_pos, check_order;
7436       boolean can_clone = FALSE;
7437       int i;
7438
7439       // check if there is any free field around current position
7440       for (i = 0; i < 8; i++)
7441       {
7442         int newx = x + check_xy[i].dx;
7443         int newy = y + check_xy[i].dy;
7444
7445         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7446         {
7447           can_clone = TRUE;
7448
7449           break;
7450         }
7451       }
7452
7453       if (can_clone)            // randomly find an element to clone
7454       {
7455         can_clone = FALSE;
7456
7457         start_pos = check_pos[RND(8)];
7458         check_order = (RND(2) ? -1 : +1);
7459
7460         for (i = 0; i < 8; i++)
7461         {
7462           int pos_raw = start_pos + i * check_order;
7463           int pos = (pos_raw + 8) % 8;
7464           int newx = x + check_xy[pos].dx;
7465           int newy = y + check_xy[pos].dy;
7466
7467           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7468           {
7469             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7470             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7471
7472             Store[x][y] = Tile[newx][newy];
7473
7474             can_clone = TRUE;
7475
7476             break;
7477           }
7478         }
7479       }
7480
7481       if (can_clone)            // randomly find a direction to move
7482       {
7483         can_clone = FALSE;
7484
7485         start_pos = check_pos[RND(8)];
7486         check_order = (RND(2) ? -1 : +1);
7487
7488         for (i = 0; i < 8; i++)
7489         {
7490           int pos_raw = start_pos + i * check_order;
7491           int pos = (pos_raw + 8) % 8;
7492           int newx = x + check_xy[pos].dx;
7493           int newy = y + check_xy[pos].dy;
7494           int new_move_dir = check_xy[pos].dir;
7495
7496           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7497           {
7498             MovDir[x][y] = new_move_dir;
7499             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7500
7501             can_clone = TRUE;
7502
7503             break;
7504           }
7505         }
7506       }
7507
7508       if (can_clone)            // cloning and moving successful
7509         return;
7510
7511       // cannot clone -- try to move towards player
7512
7513       start_pos = check_pos[MovDir[x][y] & 0x0f];
7514       check_order = (RND(2) ? -1 : +1);
7515
7516       for (i = 0; i < 3; i++)
7517       {
7518         // first check start_pos, then previous/next or (next/previous) pos
7519         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7520         int pos = (pos_raw + 8) % 8;
7521         int newx = x + check_xy[pos].dx;
7522         int newy = y + check_xy[pos].dy;
7523         int new_move_dir = check_xy[pos].dir;
7524
7525         if (IS_PLAYER(newx, newy))
7526           break;
7527
7528         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7529         {
7530           MovDir[x][y] = new_move_dir;
7531           MovDelay[x][y] = level.android_move_time * 8 + 1;
7532
7533           break;
7534         }
7535       }
7536     }
7537   }
7538   else if (move_pattern == MV_TURNING_LEFT ||
7539            move_pattern == MV_TURNING_RIGHT ||
7540            move_pattern == MV_TURNING_LEFT_RIGHT ||
7541            move_pattern == MV_TURNING_RIGHT_LEFT ||
7542            move_pattern == MV_TURNING_RANDOM ||
7543            move_pattern == MV_ALL_DIRECTIONS)
7544   {
7545     boolean can_turn_left =
7546       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7547     boolean can_turn_right =
7548       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y);
7549
7550     if (element_info[element].move_stepsize == 0)       // "not moving"
7551       return;
7552
7553     if (move_pattern == MV_TURNING_LEFT)
7554       MovDir[x][y] = left_dir;
7555     else if (move_pattern == MV_TURNING_RIGHT)
7556       MovDir[x][y] = right_dir;
7557     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7558       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7559     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7560       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7561     else if (move_pattern == MV_TURNING_RANDOM)
7562       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7563                       can_turn_right && !can_turn_left ? right_dir :
7564                       RND(2) ? left_dir : right_dir);
7565     else if (can_turn_left && can_turn_right)
7566       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7567     else if (can_turn_left)
7568       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7569     else if (can_turn_right)
7570       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7571     else
7572       MovDir[x][y] = back_dir;
7573
7574     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7575   }
7576   else if (move_pattern == MV_HORIZONTAL ||
7577            move_pattern == MV_VERTICAL)
7578   {
7579     if (move_pattern & old_move_dir)
7580       MovDir[x][y] = back_dir;
7581     else if (move_pattern == MV_HORIZONTAL)
7582       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7583     else if (move_pattern == MV_VERTICAL)
7584       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7585
7586     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7587   }
7588   else if (move_pattern & MV_ANY_DIRECTION)
7589   {
7590     MovDir[x][y] = move_pattern;
7591     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7592   }
7593   else if (move_pattern & MV_WIND_DIRECTION)
7594   {
7595     MovDir[x][y] = game.wind_direction;
7596     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7597   }
7598   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7599   {
7600     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7601       MovDir[x][y] = left_dir;
7602     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7603       MovDir[x][y] = right_dir;
7604
7605     if (MovDir[x][y] != old_move_dir)
7606       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7607   }
7608   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7609   {
7610     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7611       MovDir[x][y] = right_dir;
7612     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7613       MovDir[x][y] = left_dir;
7614
7615     if (MovDir[x][y] != old_move_dir)
7616       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7617   }
7618   else if (move_pattern == MV_TOWARDS_PLAYER ||
7619            move_pattern == MV_AWAY_FROM_PLAYER)
7620   {
7621     int attr_x = -1, attr_y = -1;
7622     int newx, newy;
7623     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7624
7625     if (game.all_players_gone)
7626     {
7627       attr_x = game.exit_x;
7628       attr_y = game.exit_y;
7629     }
7630     else
7631     {
7632       int i;
7633
7634       for (i = 0; i < MAX_PLAYERS; i++)
7635       {
7636         struct PlayerInfo *player = &stored_player[i];
7637         int jx = player->jx, jy = player->jy;
7638
7639         if (!player->active)
7640           continue;
7641
7642         if (attr_x == -1 ||
7643             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7644         {
7645           attr_x = jx;
7646           attr_y = jy;
7647         }
7648       }
7649     }
7650
7651     MovDir[x][y] = MV_NONE;
7652     if (attr_x < x)
7653       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7654     else if (attr_x > x)
7655       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7656     if (attr_y < y)
7657       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7658     else if (attr_y > y)
7659       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7660
7661     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7662
7663     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7664     {
7665       boolean first_horiz = RND(2);
7666       int new_move_dir = MovDir[x][y];
7667
7668       if (element_info[element].move_stepsize == 0)     // "not moving"
7669       {
7670         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7671         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7672
7673         return;
7674       }
7675
7676       MovDir[x][y] =
7677         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7678       Moving2Blocked(x, y, &newx, &newy);
7679
7680       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7681         return;
7682
7683       MovDir[x][y] =
7684         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7685       Moving2Blocked(x, y, &newx, &newy);
7686
7687       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7688         return;
7689
7690       MovDir[x][y] = old_move_dir;
7691     }
7692   }
7693   else if (move_pattern == MV_WHEN_PUSHED ||
7694            move_pattern == MV_WHEN_DROPPED)
7695   {
7696     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7697       MovDir[x][y] = MV_NONE;
7698
7699     MovDelay[x][y] = 0;
7700   }
7701   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7702   {
7703     struct XY *test_xy = xy_topdown;
7704     static int test_dir[4] =
7705     {
7706       MV_UP,
7707       MV_LEFT,
7708       MV_RIGHT,
7709       MV_DOWN
7710     };
7711     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7712     int move_preference = -1000000;     // start with very low preference
7713     int new_move_dir = MV_NONE;
7714     int start_test = RND(4);
7715     int i;
7716
7717     for (i = 0; i < NUM_DIRECTIONS; i++)
7718     {
7719       int j = (start_test + i) % 4;
7720       int move_dir = test_dir[j];
7721       int move_dir_preference;
7722
7723       xx = x + test_xy[j].x;
7724       yy = y + test_xy[j].y;
7725
7726       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7727           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7728       {
7729         new_move_dir = move_dir;
7730
7731         break;
7732       }
7733
7734       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7735         continue;
7736
7737       move_dir_preference = -1 * RunnerVisit[xx][yy];
7738       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7739         move_dir_preference = PlayerVisit[xx][yy];
7740
7741       if (move_dir_preference > move_preference)
7742       {
7743         // prefer field that has not been visited for the longest time
7744         move_preference = move_dir_preference;
7745         new_move_dir = move_dir;
7746       }
7747       else if (move_dir_preference == move_preference &&
7748                move_dir == old_move_dir)
7749       {
7750         // prefer last direction when all directions are preferred equally
7751         move_preference = move_dir_preference;
7752         new_move_dir = move_dir;
7753       }
7754     }
7755
7756     MovDir[x][y] = new_move_dir;
7757     if (old_move_dir != new_move_dir)
7758       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7759   }
7760 }
7761
7762 static void TurnRound(int x, int y)
7763 {
7764   int direction = MovDir[x][y];
7765
7766   TurnRoundExt(x, y);
7767
7768   GfxDir[x][y] = MovDir[x][y];
7769
7770   if (direction != MovDir[x][y])
7771     GfxFrame[x][y] = 0;
7772
7773   if (MovDelay[x][y])
7774     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7775
7776   ResetGfxFrame(x, y);
7777 }
7778
7779 static boolean JustBeingPushed(int x, int y)
7780 {
7781   int i;
7782
7783   for (i = 0; i < MAX_PLAYERS; i++)
7784   {
7785     struct PlayerInfo *player = &stored_player[i];
7786
7787     if (player->active && player->is_pushing && player->MovPos)
7788     {
7789       int next_jx = player->jx + (player->jx - player->last_jx);
7790       int next_jy = player->jy + (player->jy - player->last_jy);
7791
7792       if (x == next_jx && y == next_jy)
7793         return TRUE;
7794     }
7795   }
7796
7797   return FALSE;
7798 }
7799
7800 static void StartMoving(int x, int y)
7801 {
7802   boolean started_moving = FALSE;       // some elements can fall _and_ move
7803   int element = Tile[x][y];
7804
7805   if (Stop[x][y])
7806     return;
7807
7808   if (MovDelay[x][y] == 0)
7809     GfxAction[x][y] = ACTION_DEFAULT;
7810
7811   if (CAN_FALL(element) && y < lev_fieldy - 1)
7812   {
7813     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7814         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7815       if (JustBeingPushed(x, y))
7816         return;
7817
7818     if (element == EL_QUICKSAND_FULL)
7819     {
7820       if (IS_FREE(x, y + 1))
7821       {
7822         InitMovingField(x, y, MV_DOWN);
7823         started_moving = TRUE;
7824
7825         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7826 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7827         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7828           Store[x][y] = EL_ROCK;
7829 #else
7830         Store[x][y] = EL_ROCK;
7831 #endif
7832
7833         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7834       }
7835       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7836       {
7837         if (!MovDelay[x][y])
7838         {
7839           MovDelay[x][y] = TILEY + 1;
7840
7841           ResetGfxAnimation(x, y);
7842           ResetGfxAnimation(x, y + 1);
7843         }
7844
7845         if (MovDelay[x][y])
7846         {
7847           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7848           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7849
7850           MovDelay[x][y]--;
7851           if (MovDelay[x][y])
7852             return;
7853         }
7854
7855         Tile[x][y] = EL_QUICKSAND_EMPTY;
7856         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7857         Store[x][y + 1] = Store[x][y];
7858         Store[x][y] = 0;
7859
7860         PlayLevelSoundAction(x, y, ACTION_FILLING);
7861       }
7862       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7863       {
7864         if (!MovDelay[x][y])
7865         {
7866           MovDelay[x][y] = TILEY + 1;
7867
7868           ResetGfxAnimation(x, y);
7869           ResetGfxAnimation(x, y + 1);
7870         }
7871
7872         if (MovDelay[x][y])
7873         {
7874           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7875           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7876
7877           MovDelay[x][y]--;
7878           if (MovDelay[x][y])
7879             return;
7880         }
7881
7882         Tile[x][y] = EL_QUICKSAND_EMPTY;
7883         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7884         Store[x][y + 1] = Store[x][y];
7885         Store[x][y] = 0;
7886
7887         PlayLevelSoundAction(x, y, ACTION_FILLING);
7888       }
7889     }
7890     else if (element == EL_QUICKSAND_FAST_FULL)
7891     {
7892       if (IS_FREE(x, y + 1))
7893       {
7894         InitMovingField(x, y, MV_DOWN);
7895         started_moving = TRUE;
7896
7897         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7898 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7899         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7900           Store[x][y] = EL_ROCK;
7901 #else
7902         Store[x][y] = EL_ROCK;
7903 #endif
7904
7905         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7906       }
7907       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7908       {
7909         if (!MovDelay[x][y])
7910         {
7911           MovDelay[x][y] = TILEY + 1;
7912
7913           ResetGfxAnimation(x, y);
7914           ResetGfxAnimation(x, y + 1);
7915         }
7916
7917         if (MovDelay[x][y])
7918         {
7919           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7920           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7921
7922           MovDelay[x][y]--;
7923           if (MovDelay[x][y])
7924             return;
7925         }
7926
7927         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7928         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7929         Store[x][y + 1] = Store[x][y];
7930         Store[x][y] = 0;
7931
7932         PlayLevelSoundAction(x, y, ACTION_FILLING);
7933       }
7934       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7935       {
7936         if (!MovDelay[x][y])
7937         {
7938           MovDelay[x][y] = TILEY + 1;
7939
7940           ResetGfxAnimation(x, y);
7941           ResetGfxAnimation(x, y + 1);
7942         }
7943
7944         if (MovDelay[x][y])
7945         {
7946           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7947           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7948
7949           MovDelay[x][y]--;
7950           if (MovDelay[x][y])
7951             return;
7952         }
7953
7954         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7955         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7956         Store[x][y + 1] = Store[x][y];
7957         Store[x][y] = 0;
7958
7959         PlayLevelSoundAction(x, y, ACTION_FILLING);
7960       }
7961     }
7962     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7963              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7964     {
7965       InitMovingField(x, y, MV_DOWN);
7966       started_moving = TRUE;
7967
7968       Tile[x][y] = EL_QUICKSAND_FILLING;
7969       Store[x][y] = element;
7970
7971       PlayLevelSoundAction(x, y, ACTION_FILLING);
7972     }
7973     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7974              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7975     {
7976       InitMovingField(x, y, MV_DOWN);
7977       started_moving = TRUE;
7978
7979       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7980       Store[x][y] = element;
7981
7982       PlayLevelSoundAction(x, y, ACTION_FILLING);
7983     }
7984     else if (element == EL_MAGIC_WALL_FULL)
7985     {
7986       if (IS_FREE(x, y + 1))
7987       {
7988         InitMovingField(x, y, MV_DOWN);
7989         started_moving = TRUE;
7990
7991         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7992         Store[x][y] = EL_CHANGED(Store[x][y]);
7993       }
7994       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7995       {
7996         if (!MovDelay[x][y])
7997           MovDelay[x][y] = TILEY / 4 + 1;
7998
7999         if (MovDelay[x][y])
8000         {
8001           MovDelay[x][y]--;
8002           if (MovDelay[x][y])
8003             return;
8004         }
8005
8006         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
8007         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
8008         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8009         Store[x][y] = 0;
8010       }
8011     }
8012     else if (element == EL_BD_MAGIC_WALL_FULL)
8013     {
8014       if (IS_FREE(x, y + 1))
8015       {
8016         InitMovingField(x, y, MV_DOWN);
8017         started_moving = TRUE;
8018
8019         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8020         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8021       }
8022       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8023       {
8024         if (!MovDelay[x][y])
8025           MovDelay[x][y] = TILEY / 4 + 1;
8026
8027         if (MovDelay[x][y])
8028         {
8029           MovDelay[x][y]--;
8030           if (MovDelay[x][y])
8031             return;
8032         }
8033
8034         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8035         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8036         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8037         Store[x][y] = 0;
8038       }
8039     }
8040     else if (element == EL_DC_MAGIC_WALL_FULL)
8041     {
8042       if (IS_FREE(x, y + 1))
8043       {
8044         InitMovingField(x, y, MV_DOWN);
8045         started_moving = TRUE;
8046
8047         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8048         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8049       }
8050       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8051       {
8052         if (!MovDelay[x][y])
8053           MovDelay[x][y] = TILEY / 4 + 1;
8054
8055         if (MovDelay[x][y])
8056         {
8057           MovDelay[x][y]--;
8058           if (MovDelay[x][y])
8059             return;
8060         }
8061
8062         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8063         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8064         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8065         Store[x][y] = 0;
8066       }
8067     }
8068     else if ((CAN_PASS_MAGIC_WALL(element) &&
8069               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8070                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8071              (CAN_PASS_DC_MAGIC_WALL(element) &&
8072               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8073
8074     {
8075       InitMovingField(x, y, MV_DOWN);
8076       started_moving = TRUE;
8077
8078       Tile[x][y] =
8079         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8080          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8081          EL_DC_MAGIC_WALL_FILLING);
8082       Store[x][y] = element;
8083     }
8084     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
8085     {
8086       SplashAcid(x, y + 1);
8087
8088       InitMovingField(x, y, MV_DOWN);
8089       started_moving = TRUE;
8090
8091       Store[x][y] = EL_ACID;
8092     }
8093     else if (
8094              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8095               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8096              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8097               CAN_FALL(element) && WasJustFalling[x][y] &&
8098               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8099
8100              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8101               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8102               (Tile[x][y + 1] == EL_BLOCKED)))
8103     {
8104       /* this is needed for a special case not covered by calling "Impact()"
8105          from "ContinueMoving()": if an element moves to a tile directly below
8106          another element which was just falling on that tile (which was empty
8107          in the previous frame), the falling element above would just stop
8108          instead of smashing the element below (in previous version, the above
8109          element was just checked for "moving" instead of "falling", resulting
8110          in incorrect smashes caused by horizontal movement of the above
8111          element; also, the case of the player being the element to smash was
8112          simply not covered here... :-/ ) */
8113
8114       CheckCollision[x][y] = 0;
8115       CheckImpact[x][y] = 0;
8116
8117       Impact(x, y);
8118     }
8119     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8120     {
8121       if (MovDir[x][y] == MV_NONE)
8122       {
8123         InitMovingField(x, y, MV_DOWN);
8124         started_moving = TRUE;
8125       }
8126     }
8127     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8128     {
8129       if (WasJustFalling[x][y]) // prevent animation from being restarted
8130         MovDir[x][y] = MV_DOWN;
8131
8132       InitMovingField(x, y, MV_DOWN);
8133       started_moving = TRUE;
8134     }
8135     else if (element == EL_AMOEBA_DROP)
8136     {
8137       Tile[x][y] = EL_AMOEBA_GROWING;
8138       Store[x][y] = EL_AMOEBA_WET;
8139     }
8140     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8141               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8142              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8143              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8144     {
8145       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8146                                 (IS_FREE(x - 1, y + 1) ||
8147                                  Tile[x - 1][y + 1] == EL_ACID));
8148       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8149                                 (IS_FREE(x + 1, y + 1) ||
8150                                  Tile[x + 1][y + 1] == EL_ACID));
8151       boolean can_fall_any  = (can_fall_left || can_fall_right);
8152       boolean can_fall_both = (can_fall_left && can_fall_right);
8153       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8154
8155       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8156       {
8157         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8158           can_fall_right = FALSE;
8159         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8160           can_fall_left = FALSE;
8161         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8162           can_fall_right = FALSE;
8163         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8164           can_fall_left = FALSE;
8165
8166         can_fall_any  = (can_fall_left || can_fall_right);
8167         can_fall_both = FALSE;
8168       }
8169
8170       if (can_fall_both)
8171       {
8172         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8173           can_fall_right = FALSE;       // slip down on left side
8174         else
8175           can_fall_left = !(can_fall_right = RND(2));
8176
8177         can_fall_both = FALSE;
8178       }
8179
8180       if (can_fall_any)
8181       {
8182         // if not determined otherwise, prefer left side for slipping down
8183         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8184         started_moving = TRUE;
8185       }
8186     }
8187     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8188     {
8189       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8190       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8191       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8192       int belt_dir = game.belt_dir[belt_nr];
8193
8194       if ((belt_dir == MV_LEFT  && left_is_free) ||
8195           (belt_dir == MV_RIGHT && right_is_free))
8196       {
8197         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8198
8199         InitMovingField(x, y, belt_dir);
8200         started_moving = TRUE;
8201
8202         Pushed[x][y] = TRUE;
8203         Pushed[nextx][y] = TRUE;
8204
8205         GfxAction[x][y] = ACTION_DEFAULT;
8206       }
8207       else
8208       {
8209         MovDir[x][y] = 0;       // if element was moving, stop it
8210       }
8211     }
8212   }
8213
8214   // not "else if" because of elements that can fall and move (EL_SPRING)
8215   if (CAN_MOVE(element) && !started_moving)
8216   {
8217     int move_pattern = element_info[element].move_pattern;
8218     int newx, newy;
8219
8220     Moving2Blocked(x, y, &newx, &newy);
8221
8222     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8223       return;
8224
8225     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8226         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8227     {
8228       WasJustMoving[x][y] = 0;
8229       CheckCollision[x][y] = 0;
8230
8231       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8232
8233       if (Tile[x][y] != element)        // element has changed
8234         return;
8235     }
8236
8237     if (!MovDelay[x][y])        // start new movement phase
8238     {
8239       // all objects that can change their move direction after each step
8240       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8241
8242       if (element != EL_YAMYAM &&
8243           element != EL_DARK_YAMYAM &&
8244           element != EL_PACMAN &&
8245           !(move_pattern & MV_ANY_DIRECTION) &&
8246           move_pattern != MV_TURNING_LEFT &&
8247           move_pattern != MV_TURNING_RIGHT &&
8248           move_pattern != MV_TURNING_LEFT_RIGHT &&
8249           move_pattern != MV_TURNING_RIGHT_LEFT &&
8250           move_pattern != MV_TURNING_RANDOM)
8251       {
8252         TurnRound(x, y);
8253
8254         if (MovDelay[x][y] && (element == EL_BUG ||
8255                                element == EL_SPACESHIP ||
8256                                element == EL_SP_SNIKSNAK ||
8257                                element == EL_SP_ELECTRON ||
8258                                element == EL_MOLE))
8259           TEST_DrawLevelField(x, y);
8260       }
8261     }
8262
8263     if (MovDelay[x][y])         // wait some time before next movement
8264     {
8265       MovDelay[x][y]--;
8266
8267       if (element == EL_ROBOT ||
8268           element == EL_YAMYAM ||
8269           element == EL_DARK_YAMYAM)
8270       {
8271         DrawLevelElementAnimationIfNeeded(x, y, element);
8272         PlayLevelSoundAction(x, y, ACTION_WAITING);
8273       }
8274       else if (element == EL_SP_ELECTRON)
8275         DrawLevelElementAnimationIfNeeded(x, y, element);
8276       else if (element == EL_DRAGON)
8277       {
8278         int i;
8279         int dir = MovDir[x][y];
8280         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8281         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8282         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8283                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8284                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8285                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8286         int frame = getGraphicAnimationFrameXY(graphic, x, y);
8287
8288         GfxAction[x][y] = ACTION_ATTACKING;
8289
8290         if (IS_PLAYER(x, y))
8291           DrawPlayerField(x, y);
8292         else
8293           TEST_DrawLevelField(x, y);
8294
8295         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8296
8297         for (i = 1; i <= 3; i++)
8298         {
8299           int xx = x + i * dx;
8300           int yy = y + i * dy;
8301           int sx = SCREENX(xx);
8302           int sy = SCREENY(yy);
8303           int flame_graphic = graphic + (i - 1);
8304
8305           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8306             break;
8307
8308           if (MovDelay[x][y])
8309           {
8310             int flamed = MovingOrBlocked2Element(xx, yy);
8311
8312             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8313               Bang(xx, yy);
8314             else
8315               RemoveMovingField(xx, yy);
8316
8317             ChangeDelay[xx][yy] = 0;
8318
8319             Tile[xx][yy] = EL_FLAMES;
8320
8321             if (IN_SCR_FIELD(sx, sy))
8322             {
8323               TEST_DrawLevelFieldCrumbled(xx, yy);
8324               DrawScreenGraphic(sx, sy, flame_graphic, frame);
8325             }
8326           }
8327           else
8328           {
8329             if (Tile[xx][yy] == EL_FLAMES)
8330               Tile[xx][yy] = EL_EMPTY;
8331             TEST_DrawLevelField(xx, yy);
8332           }
8333         }
8334       }
8335
8336       if (MovDelay[x][y])       // element still has to wait some time
8337       {
8338         PlayLevelSoundAction(x, y, ACTION_WAITING);
8339
8340         return;
8341       }
8342     }
8343
8344     // now make next step
8345
8346     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8347
8348     if (DONT_COLLIDE_WITH(element) &&
8349         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8350         !PLAYER_ENEMY_PROTECTED(newx, newy))
8351     {
8352       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8353
8354       return;
8355     }
8356
8357     else if (CAN_MOVE_INTO_ACID(element) &&
8358              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8359              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8360              (MovDir[x][y] == MV_DOWN ||
8361               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8362     {
8363       SplashAcid(newx, newy);
8364       Store[x][y] = EL_ACID;
8365     }
8366     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8367     {
8368       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8369           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8370           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8371           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8372       {
8373         RemoveField(x, y);
8374         TEST_DrawLevelField(x, y);
8375
8376         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8377         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8378           DrawGraphicThruMask(SCREENX(newx), SCREENY(newy), el2img(element), 0);
8379
8380         game.friends_still_needed--;
8381         if (!game.friends_still_needed &&
8382             !game.GameOver &&
8383             game.all_players_gone)
8384           LevelSolved();
8385
8386         return;
8387       }
8388       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8389       {
8390         if (DigField(local_player, x, y, newx, newy, 0, 0, DF_DIG) == MP_MOVING)
8391           TEST_DrawLevelField(newx, newy);
8392         else
8393           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8394       }
8395       else if (!IS_FREE(newx, newy))
8396       {
8397         GfxAction[x][y] = ACTION_WAITING;
8398
8399         if (IS_PLAYER(x, y))
8400           DrawPlayerField(x, y);
8401         else
8402           TEST_DrawLevelField(x, y);
8403
8404         return;
8405       }
8406     }
8407     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8408     {
8409       if (IS_FOOD_PIG(Tile[newx][newy]))
8410       {
8411         if (IS_MOVING(newx, newy))
8412           RemoveMovingField(newx, newy);
8413         else
8414         {
8415           Tile[newx][newy] = EL_EMPTY;
8416           TEST_DrawLevelField(newx, newy);
8417         }
8418
8419         PlayLevelSound(x, y, SND_PIG_DIGGING);
8420       }
8421       else if (!IS_FREE(newx, newy))
8422       {
8423         if (IS_PLAYER(x, y))
8424           DrawPlayerField(x, y);
8425         else
8426           TEST_DrawLevelField(x, y);
8427
8428         return;
8429       }
8430     }
8431     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8432     {
8433       if (Store[x][y] != EL_EMPTY)
8434       {
8435         boolean can_clone = FALSE;
8436         int xx, yy;
8437
8438         // check if element to clone is still there
8439         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8440         {
8441           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8442           {
8443             can_clone = TRUE;
8444
8445             break;
8446           }
8447         }
8448
8449         // cannot clone or target field not free anymore -- do not clone
8450         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8451           Store[x][y] = EL_EMPTY;
8452       }
8453
8454       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8455       {
8456         if (IS_MV_DIAGONAL(MovDir[x][y]))
8457         {
8458           int diagonal_move_dir = MovDir[x][y];
8459           int stored = Store[x][y];
8460           int change_delay = 8;
8461           int graphic;
8462
8463           // android is moving diagonally
8464
8465           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8466
8467           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8468           GfxElement[x][y] = EL_EMC_ANDROID;
8469           GfxAction[x][y] = ACTION_SHRINKING;
8470           GfxDir[x][y] = diagonal_move_dir;
8471           ChangeDelay[x][y] = change_delay;
8472
8473           if (Store[x][y] == EL_EMPTY)
8474             Store[x][y] = GfxElementEmpty[x][y];
8475
8476           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8477                                    GfxDir[x][y]);
8478
8479           DrawLevelGraphicAnimation(x, y, graphic);
8480           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8481
8482           if (Tile[newx][newy] == EL_ACID)
8483           {
8484             SplashAcid(newx, newy);
8485
8486             return;
8487           }
8488
8489           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8490
8491           Store[newx][newy] = EL_EMC_ANDROID;
8492           GfxElement[newx][newy] = EL_EMC_ANDROID;
8493           GfxAction[newx][newy] = ACTION_GROWING;
8494           GfxDir[newx][newy] = diagonal_move_dir;
8495           ChangeDelay[newx][newy] = change_delay;
8496
8497           graphic = el_act_dir2img(GfxElement[newx][newy],
8498                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8499
8500           DrawLevelGraphicAnimation(newx, newy, graphic);
8501           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8502
8503           return;
8504         }
8505         else
8506         {
8507           Tile[newx][newy] = EL_EMPTY;
8508           TEST_DrawLevelField(newx, newy);
8509
8510           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8511         }
8512       }
8513       else if (!IS_FREE(newx, newy))
8514       {
8515         return;
8516       }
8517     }
8518     else if (IS_CUSTOM_ELEMENT(element) &&
8519              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8520     {
8521       if (!DigFieldByCE(newx, newy, element))
8522         return;
8523
8524       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8525       {
8526         RunnerVisit[x][y] = FrameCounter;
8527         PlayerVisit[x][y] /= 8;         // expire player visit path
8528       }
8529     }
8530     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8531     {
8532       if (!IS_FREE(newx, newy))
8533       {
8534         if (IS_PLAYER(x, y))
8535           DrawPlayerField(x, y);
8536         else
8537           TEST_DrawLevelField(x, y);
8538
8539         return;
8540       }
8541       else
8542       {
8543         boolean wanna_flame = !RND(10);
8544         int dx = newx - x, dy = newy - y;
8545         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8546         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8547         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8548                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8549         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8550                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8551
8552         if ((wanna_flame ||
8553              IS_CLASSIC_ENEMY(element1) ||
8554              IS_CLASSIC_ENEMY(element2)) &&
8555             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8556             element1 != EL_FLAMES && element2 != EL_FLAMES)
8557         {
8558           ResetGfxAnimation(x, y);
8559           GfxAction[x][y] = ACTION_ATTACKING;
8560
8561           if (IS_PLAYER(x, y))
8562             DrawPlayerField(x, y);
8563           else
8564             TEST_DrawLevelField(x, y);
8565
8566           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8567
8568           MovDelay[x][y] = 50;
8569
8570           Tile[newx][newy] = EL_FLAMES;
8571           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8572             Tile[newx1][newy1] = EL_FLAMES;
8573           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8574             Tile[newx2][newy2] = EL_FLAMES;
8575
8576           return;
8577         }
8578       }
8579     }
8580     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8581              Tile[newx][newy] == EL_DIAMOND)
8582     {
8583       if (IS_MOVING(newx, newy))
8584         RemoveMovingField(newx, newy);
8585       else
8586       {
8587         Tile[newx][newy] = EL_EMPTY;
8588         TEST_DrawLevelField(newx, newy);
8589       }
8590
8591       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8592     }
8593     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8594              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8595     {
8596       if (AmoebaNr[newx][newy])
8597       {
8598         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8599         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8600             Tile[newx][newy] == EL_BD_AMOEBA)
8601           AmoebaCnt[AmoebaNr[newx][newy]]--;
8602       }
8603
8604       if (IS_MOVING(newx, newy))
8605       {
8606         RemoveMovingField(newx, newy);
8607       }
8608       else
8609       {
8610         Tile[newx][newy] = EL_EMPTY;
8611         TEST_DrawLevelField(newx, newy);
8612       }
8613
8614       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8615     }
8616     else if ((element == EL_PACMAN || element == EL_MOLE)
8617              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8618     {
8619       if (AmoebaNr[newx][newy])
8620       {
8621         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8622         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8623             Tile[newx][newy] == EL_BD_AMOEBA)
8624           AmoebaCnt[AmoebaNr[newx][newy]]--;
8625       }
8626
8627       if (element == EL_MOLE)
8628       {
8629         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8630         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8631
8632         ResetGfxAnimation(x, y);
8633         GfxAction[x][y] = ACTION_DIGGING;
8634         TEST_DrawLevelField(x, y);
8635
8636         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8637
8638         return;                         // wait for shrinking amoeba
8639       }
8640       else      // element == EL_PACMAN
8641       {
8642         Tile[newx][newy] = EL_EMPTY;
8643         TEST_DrawLevelField(newx, newy);
8644         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8645       }
8646     }
8647     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8648              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8649               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8650     {
8651       // wait for shrinking amoeba to completely disappear
8652       return;
8653     }
8654     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8655     {
8656       // object was running against a wall
8657
8658       TurnRound(x, y);
8659
8660       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8661         DrawLevelElementAnimation(x, y, element);
8662
8663       if (DONT_TOUCH(element))
8664         TestIfBadThingTouchesPlayer(x, y);
8665
8666       return;
8667     }
8668
8669     InitMovingField(x, y, MovDir[x][y]);
8670
8671     PlayLevelSoundAction(x, y, ACTION_MOVING);
8672   }
8673
8674   if (MovDir[x][y])
8675     ContinueMoving(x, y);
8676 }
8677
8678 void ContinueMoving(int x, int y)
8679 {
8680   int element = Tile[x][y];
8681   struct ElementInfo *ei = &element_info[element];
8682   int direction = MovDir[x][y];
8683   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8684   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8685   int newx = x + dx, newy = y + dy;
8686   int stored = Store[x][y];
8687   int stored_new = Store[newx][newy];
8688   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8689   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8690   boolean last_line = (newy == lev_fieldy - 1);
8691   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8692
8693   if (pushed_by_player)         // special case: moving object pushed by player
8694   {
8695     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x, y)->MovPos));
8696   }
8697   else if (use_step_delay)      // special case: moving object has step delay
8698   {
8699     if (!MovDelay[x][y])
8700       MovPos[x][y] += getElementMoveStepsize(x, y);
8701
8702     if (MovDelay[x][y])
8703       MovDelay[x][y]--;
8704     else
8705       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8706
8707     if (MovDelay[x][y])
8708     {
8709       TEST_DrawLevelField(x, y);
8710
8711       return;   // element is still waiting
8712     }
8713   }
8714   else                          // normal case: generically moving object
8715   {
8716     MovPos[x][y] += getElementMoveStepsize(x, y);
8717   }
8718
8719   if (ABS(MovPos[x][y]) < TILEX)
8720   {
8721     TEST_DrawLevelField(x, y);
8722
8723     return;     // element is still moving
8724   }
8725
8726   // element reached destination field
8727
8728   Tile[x][y] = EL_EMPTY;
8729   Tile[newx][newy] = element;
8730   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8731
8732   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8733   {
8734     element = Tile[newx][newy] = EL_ACID;
8735   }
8736   else if (element == EL_MOLE)
8737   {
8738     Tile[x][y] = EL_SAND;
8739
8740     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8741   }
8742   else if (element == EL_QUICKSAND_FILLING)
8743   {
8744     element = Tile[newx][newy] = get_next_element(element);
8745     Store[newx][newy] = Store[x][y];
8746   }
8747   else if (element == EL_QUICKSAND_EMPTYING)
8748   {
8749     Tile[x][y] = get_next_element(element);
8750     element = Tile[newx][newy] = Store[x][y];
8751   }
8752   else if (element == EL_QUICKSAND_FAST_FILLING)
8753   {
8754     element = Tile[newx][newy] = get_next_element(element);
8755     Store[newx][newy] = Store[x][y];
8756   }
8757   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8758   {
8759     Tile[x][y] = get_next_element(element);
8760     element = Tile[newx][newy] = Store[x][y];
8761   }
8762   else if (element == EL_MAGIC_WALL_FILLING)
8763   {
8764     element = Tile[newx][newy] = get_next_element(element);
8765     if (!game.magic_wall_active)
8766       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8767     Store[newx][newy] = Store[x][y];
8768   }
8769   else if (element == EL_MAGIC_WALL_EMPTYING)
8770   {
8771     Tile[x][y] = get_next_element(element);
8772     if (!game.magic_wall_active)
8773       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8774     element = Tile[newx][newy] = Store[x][y];
8775
8776     InitField(newx, newy, FALSE);
8777   }
8778   else if (element == EL_BD_MAGIC_WALL_FILLING)
8779   {
8780     element = Tile[newx][newy] = get_next_element(element);
8781     if (!game.magic_wall_active)
8782       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8783     Store[newx][newy] = Store[x][y];
8784   }
8785   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8786   {
8787     Tile[x][y] = get_next_element(element);
8788     if (!game.magic_wall_active)
8789       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8790     element = Tile[newx][newy] = Store[x][y];
8791
8792     InitField(newx, newy, FALSE);
8793   }
8794   else if (element == EL_DC_MAGIC_WALL_FILLING)
8795   {
8796     element = Tile[newx][newy] = get_next_element(element);
8797     if (!game.magic_wall_active)
8798       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8799     Store[newx][newy] = Store[x][y];
8800   }
8801   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8802   {
8803     Tile[x][y] = get_next_element(element);
8804     if (!game.magic_wall_active)
8805       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8806     element = Tile[newx][newy] = Store[x][y];
8807
8808     InitField(newx, newy, FALSE);
8809   }
8810   else if (element == EL_AMOEBA_DROPPING)
8811   {
8812     Tile[x][y] = get_next_element(element);
8813     element = Tile[newx][newy] = Store[x][y];
8814   }
8815   else if (element == EL_SOKOBAN_OBJECT)
8816   {
8817     if (Back[x][y])
8818       Tile[x][y] = Back[x][y];
8819
8820     if (Back[newx][newy])
8821       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8822
8823     Back[x][y] = Back[newx][newy] = 0;
8824   }
8825
8826   Store[x][y] = EL_EMPTY;
8827   MovPos[x][y] = 0;
8828   MovDir[x][y] = 0;
8829   MovDelay[x][y] = 0;
8830
8831   MovDelay[newx][newy] = 0;
8832
8833   if (CAN_CHANGE_OR_HAS_ACTION(element))
8834   {
8835     // copy element change control values to new field
8836     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8837     ChangePage[newx][newy]  = ChangePage[x][y];
8838     ChangeCount[newx][newy] = ChangeCount[x][y];
8839     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8840   }
8841
8842   CustomValue[newx][newy] = CustomValue[x][y];
8843
8844   ChangeDelay[x][y] = 0;
8845   ChangePage[x][y] = -1;
8846   ChangeCount[x][y] = 0;
8847   ChangeEvent[x][y] = -1;
8848
8849   CustomValue[x][y] = 0;
8850
8851   // copy animation control values to new field
8852   GfxFrame[newx][newy]  = GfxFrame[x][y];
8853   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8854   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8855   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8856
8857   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8858
8859   // some elements can leave other elements behind after moving
8860   if (ei->move_leave_element != EL_EMPTY &&
8861       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8862       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8863   {
8864     int move_leave_element = ei->move_leave_element;
8865
8866     // this makes it possible to leave the removed element again
8867     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8868       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8869
8870     Tile[x][y] = move_leave_element;
8871
8872     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8873       MovDir[x][y] = direction;
8874
8875     InitField(x, y, FALSE);
8876
8877     if (GFX_CRUMBLED(Tile[x][y]))
8878       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8879
8880     if (IS_PLAYER_ELEMENT(move_leave_element))
8881       RelocatePlayer(x, y, move_leave_element);
8882   }
8883
8884   // do this after checking for left-behind element
8885   ResetGfxAnimation(x, y);      // reset animation values for old field
8886
8887   if (!CAN_MOVE(element) ||
8888       (CAN_FALL(element) && direction == MV_DOWN &&
8889        (element == EL_SPRING ||
8890         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8891         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8892     GfxDir[x][y] = MovDir[newx][newy] = 0;
8893
8894   TEST_DrawLevelField(x, y);
8895   TEST_DrawLevelField(newx, newy);
8896
8897   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8898
8899   // prevent pushed element from moving on in pushed direction
8900   if (pushed_by_player && CAN_MOVE(element) &&
8901       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8902       !(element_info[element].move_pattern & direction))
8903     TurnRound(newx, newy);
8904
8905   // prevent elements on conveyor belt from moving on in last direction
8906   if (pushed_by_conveyor && CAN_FALL(element) &&
8907       direction & MV_HORIZONTAL)
8908     MovDir[newx][newy] = 0;
8909
8910   if (!pushed_by_player)
8911   {
8912     int nextx = newx + dx, nexty = newy + dy;
8913     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8914
8915     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8916
8917     if (CAN_FALL(element) && direction == MV_DOWN)
8918       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8919
8920     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8921       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8922
8923     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8924       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8925   }
8926
8927   if (DONT_TOUCH(element))      // object may be nasty to player or others
8928   {
8929     TestIfBadThingTouchesPlayer(newx, newy);
8930     TestIfBadThingTouchesFriend(newx, newy);
8931
8932     if (!IS_CUSTOM_ELEMENT(element))
8933       TestIfBadThingTouchesOtherBadThing(newx, newy);
8934   }
8935   else if (element == EL_PENGUIN)
8936     TestIfFriendTouchesBadThing(newx, newy);
8937
8938   if (DONT_GET_HIT_BY(element))
8939   {
8940     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8941   }
8942
8943   // give the player one last chance (one more frame) to move away
8944   if (CAN_FALL(element) && direction == MV_DOWN &&
8945       (last_line || (!IS_FREE(x, newy + 1) &&
8946                      (!IS_PLAYER(x, newy + 1) ||
8947                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8948     Impact(x, newy);
8949
8950   if (pushed_by_player && !game.use_change_when_pushing_bug)
8951   {
8952     int push_side = MV_DIR_OPPOSITE(direction);
8953     struct PlayerInfo *player = PLAYERINFO(x, y);
8954
8955     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8956                                player->index_bit, push_side);
8957     CheckTriggeredElementChangeByPlayer(newx, newy, element, CE_PLAYER_PUSHES_X,
8958                                         player->index_bit, push_side);
8959   }
8960
8961   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8962     MovDelay[newx][newy] = 1;
8963
8964   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8965
8966   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8967   TestIfElementHitsCustomElement(newx, newy, direction);
8968   TestIfPlayerTouchesCustomElement(newx, newy);
8969   TestIfElementTouchesCustomElement(newx, newy);
8970
8971   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8972       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8973     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8974                              MV_DIR_OPPOSITE(direction));
8975 }
8976
8977 int AmoebaNeighbourNr(int ax, int ay)
8978 {
8979   int i;
8980   int element = Tile[ax][ay];
8981   int group_nr = 0;
8982   struct XY *xy = xy_topdown;
8983
8984   for (i = 0; i < NUM_DIRECTIONS; i++)
8985   {
8986     int x = ax + xy[i].x;
8987     int y = ay + xy[i].y;
8988
8989     if (!IN_LEV_FIELD(x, y))
8990       continue;
8991
8992     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8993       group_nr = AmoebaNr[x][y];
8994   }
8995
8996   return group_nr;
8997 }
8998
8999 static void AmoebaMerge(int ax, int ay)
9000 {
9001   int i, x, y, xx, yy;
9002   int new_group_nr = AmoebaNr[ax][ay];
9003   struct XY *xy = xy_topdown;
9004
9005   if (new_group_nr == 0)
9006     return;
9007
9008   for (i = 0; i < NUM_DIRECTIONS; i++)
9009   {
9010     x = ax + xy[i].x;
9011     y = ay + xy[i].y;
9012
9013     if (!IN_LEV_FIELD(x, y))
9014       continue;
9015
9016     if ((Tile[x][y] == EL_AMOEBA_FULL ||
9017          Tile[x][y] == EL_BD_AMOEBA ||
9018          Tile[x][y] == EL_AMOEBA_DEAD) &&
9019         AmoebaNr[x][y] != new_group_nr)
9020     {
9021       int old_group_nr = AmoebaNr[x][y];
9022
9023       if (old_group_nr == 0)
9024         return;
9025
9026       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9027       AmoebaCnt[old_group_nr] = 0;
9028       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9029       AmoebaCnt2[old_group_nr] = 0;
9030
9031       SCAN_PLAYFIELD(xx, yy)
9032       {
9033         if (AmoebaNr[xx][yy] == old_group_nr)
9034           AmoebaNr[xx][yy] = new_group_nr;
9035       }
9036     }
9037   }
9038 }
9039
9040 void AmoebaToDiamond(int ax, int ay)
9041 {
9042   int i, x, y;
9043
9044   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
9045   {
9046     int group_nr = AmoebaNr[ax][ay];
9047
9048 #ifdef DEBUG
9049     if (group_nr == 0)
9050     {
9051       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
9052       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
9053
9054       return;
9055     }
9056 #endif
9057
9058     SCAN_PLAYFIELD(x, y)
9059     {
9060       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9061       {
9062         AmoebaNr[x][y] = 0;
9063         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
9064       }
9065     }
9066
9067     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9068                             SND_AMOEBA_TURNING_TO_GEM :
9069                             SND_AMOEBA_TURNING_TO_ROCK));
9070     Bang(ax, ay);
9071   }
9072   else
9073   {
9074     struct XY *xy = xy_topdown;
9075
9076     for (i = 0; i < NUM_DIRECTIONS; i++)
9077     {
9078       x = ax + xy[i].x;
9079       y = ay + xy[i].y;
9080
9081       if (!IN_LEV_FIELD(x, y))
9082         continue;
9083
9084       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9085       {
9086         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9087                               SND_AMOEBA_TURNING_TO_GEM :
9088                               SND_AMOEBA_TURNING_TO_ROCK));
9089         Bang(x, y);
9090       }
9091     }
9092   }
9093 }
9094
9095 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9096 {
9097   int x, y;
9098   int group_nr = AmoebaNr[ax][ay];
9099   boolean done = FALSE;
9100
9101 #ifdef DEBUG
9102   if (group_nr == 0)
9103   {
9104     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9105     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9106
9107     return;
9108   }
9109 #endif
9110
9111   SCAN_PLAYFIELD(x, y)
9112   {
9113     if (AmoebaNr[x][y] == group_nr &&
9114         (Tile[x][y] == EL_AMOEBA_DEAD ||
9115          Tile[x][y] == EL_BD_AMOEBA ||
9116          Tile[x][y] == EL_AMOEBA_GROWING))
9117     {
9118       AmoebaNr[x][y] = 0;
9119       Tile[x][y] = new_element;
9120       InitField(x, y, FALSE);
9121       TEST_DrawLevelField(x, y);
9122       done = TRUE;
9123     }
9124   }
9125
9126   if (done)
9127     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9128                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9129                             SND_BD_AMOEBA_TURNING_TO_GEM));
9130 }
9131
9132 static void AmoebaGrowing(int x, int y)
9133 {
9134   static DelayCounter sound_delay = { 0 };
9135
9136   if (!MovDelay[x][y])          // start new growing cycle
9137   {
9138     MovDelay[x][y] = 7;
9139
9140     if (DelayReached(&sound_delay))
9141     {
9142       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9143       sound_delay.value = 30;
9144     }
9145   }
9146
9147   if (MovDelay[x][y])           // wait some time before growing bigger
9148   {
9149     MovDelay[x][y]--;
9150     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9151     {
9152       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9153                                            6 - MovDelay[x][y]);
9154
9155       DrawLevelGraphic(x, y, IMG_AMOEBA_GROWING, frame);
9156     }
9157
9158     if (!MovDelay[x][y])
9159     {
9160       Tile[x][y] = Store[x][y];
9161       Store[x][y] = 0;
9162       TEST_DrawLevelField(x, y);
9163     }
9164   }
9165 }
9166
9167 static void AmoebaShrinking(int x, int y)
9168 {
9169   static DelayCounter sound_delay = { 0 };
9170
9171   if (!MovDelay[x][y])          // start new shrinking cycle
9172   {
9173     MovDelay[x][y] = 7;
9174
9175     if (DelayReached(&sound_delay))
9176       sound_delay.value = 30;
9177   }
9178
9179   if (MovDelay[x][y])           // wait some time before shrinking
9180   {
9181     MovDelay[x][y]--;
9182     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9183     {
9184       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9185                                            6 - MovDelay[x][y]);
9186
9187       DrawLevelGraphic(x, y, IMG_AMOEBA_SHRINKING, frame);
9188     }
9189
9190     if (!MovDelay[x][y])
9191     {
9192       Tile[x][y] = EL_EMPTY;
9193       TEST_DrawLevelField(x, y);
9194
9195       // don't let mole enter this field in this cycle;
9196       // (give priority to objects falling to this field from above)
9197       Stop[x][y] = TRUE;
9198     }
9199   }
9200 }
9201
9202 static void AmoebaReproduce(int ax, int ay)
9203 {
9204   int i;
9205   int element = Tile[ax][ay];
9206   int graphic = el2img(element);
9207   int newax = ax, neway = ay;
9208   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9209   struct XY *xy = xy_topdown;
9210
9211   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9212   {
9213     Tile[ax][ay] = EL_AMOEBA_DEAD;
9214     TEST_DrawLevelField(ax, ay);
9215     return;
9216   }
9217
9218   if (IS_ANIMATED(graphic))
9219     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9220
9221   if (!MovDelay[ax][ay])        // start making new amoeba field
9222     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9223
9224   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9225   {
9226     MovDelay[ax][ay]--;
9227     if (MovDelay[ax][ay])
9228       return;
9229   }
9230
9231   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9232   {
9233     int start = RND(4);
9234     int x = ax + xy[start].x;
9235     int y = ay + xy[start].y;
9236
9237     if (!IN_LEV_FIELD(x, y))
9238       return;
9239
9240     if (IS_FREE(x, y) ||
9241         CAN_GROW_INTO(Tile[x][y]) ||
9242         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9243         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9244     {
9245       newax = x;
9246       neway = y;
9247     }
9248
9249     if (newax == ax && neway == ay)
9250       return;
9251   }
9252   else                          // normal or "filled" (BD style) amoeba
9253   {
9254     int start = RND(4);
9255     boolean waiting_for_player = FALSE;
9256
9257     for (i = 0; i < NUM_DIRECTIONS; i++)
9258     {
9259       int j = (start + i) % 4;
9260       int x = ax + xy[j].x;
9261       int y = ay + xy[j].y;
9262
9263       if (!IN_LEV_FIELD(x, y))
9264         continue;
9265
9266       if (IS_FREE(x, y) ||
9267           CAN_GROW_INTO(Tile[x][y]) ||
9268           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9269           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9270       {
9271         newax = x;
9272         neway = y;
9273         break;
9274       }
9275       else if (IS_PLAYER(x, y))
9276         waiting_for_player = TRUE;
9277     }
9278
9279     if (newax == ax && neway == ay)             // amoeba cannot grow
9280     {
9281       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9282       {
9283         Tile[ax][ay] = EL_AMOEBA_DEAD;
9284         TEST_DrawLevelField(ax, ay);
9285         AmoebaCnt[AmoebaNr[ax][ay]]--;
9286
9287         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9288         {
9289           if (element == EL_AMOEBA_FULL)
9290             AmoebaToDiamond(ax, ay);
9291           else if (element == EL_BD_AMOEBA)
9292             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9293         }
9294       }
9295       return;
9296     }
9297     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9298     {
9299       // amoeba gets larger by growing in some direction
9300
9301       int new_group_nr = AmoebaNr[ax][ay];
9302
9303 #ifdef DEBUG
9304   if (new_group_nr == 0)
9305   {
9306     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9307           newax, neway);
9308     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9309
9310     return;
9311   }
9312 #endif
9313
9314       AmoebaNr[newax][neway] = new_group_nr;
9315       AmoebaCnt[new_group_nr]++;
9316       AmoebaCnt2[new_group_nr]++;
9317
9318       // if amoeba touches other amoeba(s) after growing, unify them
9319       AmoebaMerge(newax, neway);
9320
9321       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9322       {
9323         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9324         return;
9325       }
9326     }
9327   }
9328
9329   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9330       (neway == lev_fieldy - 1 && newax != ax))
9331   {
9332     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9333     Store[newax][neway] = element;
9334   }
9335   else if (neway == ay || element == EL_EMC_DRIPPER)
9336   {
9337     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9338
9339     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9340   }
9341   else
9342   {
9343     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9344     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9345     Store[ax][ay] = EL_AMOEBA_DROP;
9346     ContinueMoving(ax, ay);
9347     return;
9348   }
9349
9350   TEST_DrawLevelField(newax, neway);
9351 }
9352
9353 static void Life(int ax, int ay)
9354 {
9355   int x1, y1, x2, y2;
9356   int life_time = 40;
9357   int element = Tile[ax][ay];
9358   int graphic = el2img(element);
9359   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9360                          level.biomaze);
9361   boolean changed = FALSE;
9362
9363   if (IS_ANIMATED(graphic))
9364     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9365
9366   if (Stop[ax][ay])
9367     return;
9368
9369   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9370     MovDelay[ax][ay] = life_time;
9371
9372   if (MovDelay[ax][ay])         // wait some time before next cycle
9373   {
9374     MovDelay[ax][ay]--;
9375     if (MovDelay[ax][ay])
9376       return;
9377   }
9378
9379   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9380   {
9381     int xx = ax+x1, yy = ay+y1;
9382     int old_element = Tile[xx][yy];
9383     int num_neighbours = 0;
9384
9385     if (!IN_LEV_FIELD(xx, yy))
9386       continue;
9387
9388     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9389     {
9390       int x = xx+x2, y = yy+y2;
9391
9392       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9393         continue;
9394
9395       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9396       boolean is_neighbour = FALSE;
9397
9398       if (level.use_life_bugs)
9399         is_neighbour =
9400           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9401            (IS_FREE(x, y)                             &&  Stop[x][y]));
9402       else
9403         is_neighbour =
9404           (Last[x][y] == element || is_player_cell);
9405
9406       if (is_neighbour)
9407         num_neighbours++;
9408     }
9409
9410     boolean is_free = FALSE;
9411
9412     if (level.use_life_bugs)
9413       is_free = (IS_FREE(xx, yy));
9414     else
9415       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9416
9417     if (xx == ax && yy == ay)           // field in the middle
9418     {
9419       if (num_neighbours < life_parameter[0] ||
9420           num_neighbours > life_parameter[1])
9421       {
9422         Tile[xx][yy] = EL_EMPTY;
9423         if (Tile[xx][yy] != old_element)
9424           TEST_DrawLevelField(xx, yy);
9425         Stop[xx][yy] = TRUE;
9426         changed = TRUE;
9427       }
9428     }
9429     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9430     {                                   // free border field
9431       if (num_neighbours >= life_parameter[2] &&
9432           num_neighbours <= life_parameter[3])
9433       {
9434         Tile[xx][yy] = element;
9435         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time - 1);
9436         if (Tile[xx][yy] != old_element)
9437           TEST_DrawLevelField(xx, yy);
9438         Stop[xx][yy] = TRUE;
9439         changed = TRUE;
9440       }
9441     }
9442   }
9443
9444   if (changed)
9445     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9446                    SND_GAME_OF_LIFE_GROWING);
9447 }
9448
9449 static void InitRobotWheel(int x, int y)
9450 {
9451   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9452 }
9453
9454 static void RunRobotWheel(int x, int y)
9455 {
9456   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9457 }
9458
9459 static void StopRobotWheel(int x, int y)
9460 {
9461   if (game.robot_wheel_x == x &&
9462       game.robot_wheel_y == y)
9463   {
9464     game.robot_wheel_x = -1;
9465     game.robot_wheel_y = -1;
9466     game.robot_wheel_active = FALSE;
9467   }
9468 }
9469
9470 static void InitTimegateWheel(int x, int y)
9471 {
9472   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9473 }
9474
9475 static void RunTimegateWheel(int x, int y)
9476 {
9477   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9478 }
9479
9480 static void InitMagicBallDelay(int x, int y)
9481 {
9482   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9483 }
9484
9485 static void ActivateMagicBall(int bx, int by)
9486 {
9487   int x, y;
9488
9489   if (level.ball_random)
9490   {
9491     int pos_border = RND(8);    // select one of the eight border elements
9492     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9493     int xx = pos_content % 3;
9494     int yy = pos_content / 3;
9495
9496     x = bx - 1 + xx;
9497     y = by - 1 + yy;
9498
9499     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9500       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9501   }
9502   else
9503   {
9504     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9505     {
9506       int xx = x - bx + 1;
9507       int yy = y - by + 1;
9508
9509       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9510         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9511     }
9512   }
9513
9514   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9515 }
9516
9517 static void CheckExit(int x, int y)
9518 {
9519   if (game.gems_still_needed > 0 ||
9520       game.sokoban_fields_still_needed > 0 ||
9521       game.sokoban_objects_still_needed > 0 ||
9522       game.lights_still_needed > 0)
9523   {
9524     int element = Tile[x][y];
9525     int graphic = el2img(element);
9526
9527     if (IS_ANIMATED(graphic))
9528       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9529
9530     return;
9531   }
9532
9533   // do not re-open exit door closed after last player
9534   if (game.all_players_gone)
9535     return;
9536
9537   Tile[x][y] = EL_EXIT_OPENING;
9538
9539   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9540 }
9541
9542 static void CheckExitEM(int x, int y)
9543 {
9544   if (game.gems_still_needed > 0 ||
9545       game.sokoban_fields_still_needed > 0 ||
9546       game.sokoban_objects_still_needed > 0 ||
9547       game.lights_still_needed > 0)
9548   {
9549     int element = Tile[x][y];
9550     int graphic = el2img(element);
9551
9552     if (IS_ANIMATED(graphic))
9553       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9554
9555     return;
9556   }
9557
9558   // do not re-open exit door closed after last player
9559   if (game.all_players_gone)
9560     return;
9561
9562   Tile[x][y] = EL_EM_EXIT_OPENING;
9563
9564   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9565 }
9566
9567 static void CheckExitSteel(int x, int y)
9568 {
9569   if (game.gems_still_needed > 0 ||
9570       game.sokoban_fields_still_needed > 0 ||
9571       game.sokoban_objects_still_needed > 0 ||
9572       game.lights_still_needed > 0)
9573   {
9574     int element = Tile[x][y];
9575     int graphic = el2img(element);
9576
9577     if (IS_ANIMATED(graphic))
9578       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9579
9580     return;
9581   }
9582
9583   // do not re-open exit door closed after last player
9584   if (game.all_players_gone)
9585     return;
9586
9587   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9588
9589   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9590 }
9591
9592 static void CheckExitSteelEM(int x, int y)
9593 {
9594   if (game.gems_still_needed > 0 ||
9595       game.sokoban_fields_still_needed > 0 ||
9596       game.sokoban_objects_still_needed > 0 ||
9597       game.lights_still_needed > 0)
9598   {
9599     int element = Tile[x][y];
9600     int graphic = el2img(element);
9601
9602     if (IS_ANIMATED(graphic))
9603       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9604
9605     return;
9606   }
9607
9608   // do not re-open exit door closed after last player
9609   if (game.all_players_gone)
9610     return;
9611
9612   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9613
9614   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9615 }
9616
9617 static void CheckExitSP(int x, int y)
9618 {
9619   if (game.gems_still_needed > 0)
9620   {
9621     int element = Tile[x][y];
9622     int graphic = el2img(element);
9623
9624     if (IS_ANIMATED(graphic))
9625       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9626
9627     return;
9628   }
9629
9630   // do not re-open exit door closed after last player
9631   if (game.all_players_gone)
9632     return;
9633
9634   Tile[x][y] = EL_SP_EXIT_OPENING;
9635
9636   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9637 }
9638
9639 static void CloseAllOpenTimegates(void)
9640 {
9641   int x, y;
9642
9643   SCAN_PLAYFIELD(x, y)
9644   {
9645     int element = Tile[x][y];
9646
9647     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9648     {
9649       Tile[x][y] = EL_TIMEGATE_CLOSING;
9650
9651       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9652     }
9653   }
9654 }
9655
9656 static void DrawTwinkleOnField(int x, int y)
9657 {
9658   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9659     return;
9660
9661   if (Tile[x][y] == EL_BD_DIAMOND)
9662     return;
9663
9664   if (MovDelay[x][y] == 0)      // next animation frame
9665     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9666
9667   if (MovDelay[x][y] != 0)      // wait some time before next frame
9668   {
9669     MovDelay[x][y]--;
9670
9671     DrawLevelElementAnimation(x, y, Tile[x][y]);
9672
9673     if (MovDelay[x][y] != 0)
9674     {
9675       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9676                                            10 - MovDelay[x][y]);
9677
9678       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9679     }
9680   }
9681 }
9682
9683 static void WallGrowing(int x, int y)
9684 {
9685   int delay = 6;
9686
9687   if (!MovDelay[x][y])          // next animation frame
9688     MovDelay[x][y] = 3 * delay;
9689
9690   if (MovDelay[x][y])           // wait some time before next frame
9691   {
9692     MovDelay[x][y]--;
9693
9694     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9695     {
9696       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9697       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9698
9699       DrawLevelGraphic(x, y, graphic, frame);
9700     }
9701
9702     if (!MovDelay[x][y])
9703     {
9704       if (MovDir[x][y] == MV_LEFT)
9705       {
9706         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9707           TEST_DrawLevelField(x - 1, y);
9708       }
9709       else if (MovDir[x][y] == MV_RIGHT)
9710       {
9711         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9712           TEST_DrawLevelField(x + 1, y);
9713       }
9714       else if (MovDir[x][y] == MV_UP)
9715       {
9716         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9717           TEST_DrawLevelField(x, y - 1);
9718       }
9719       else
9720       {
9721         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9722           TEST_DrawLevelField(x, y + 1);
9723       }
9724
9725       Tile[x][y] = Store[x][y];
9726       Store[x][y] = 0;
9727       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9728       TEST_DrawLevelField(x, y);
9729     }
9730   }
9731 }
9732
9733 static void CheckWallGrowing(int ax, int ay)
9734 {
9735   int element = Tile[ax][ay];
9736   int graphic = el2img(element);
9737   boolean free_top    = FALSE;
9738   boolean free_bottom = FALSE;
9739   boolean free_left   = FALSE;
9740   boolean free_right  = FALSE;
9741   boolean stop_top    = FALSE;
9742   boolean stop_bottom = FALSE;
9743   boolean stop_left   = FALSE;
9744   boolean stop_right  = FALSE;
9745   boolean new_wall    = FALSE;
9746
9747   boolean is_steelwall  = (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9748                            element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9749                            element == EL_EXPANDABLE_STEELWALL_ANY);
9750
9751   boolean grow_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9752                              element == EL_EXPANDABLE_WALL_ANY ||
9753                              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9754                              element == EL_EXPANDABLE_STEELWALL_ANY);
9755
9756   boolean grow_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9757                              element == EL_EXPANDABLE_WALL_ANY ||
9758                              element == EL_EXPANDABLE_WALL ||
9759                              element == EL_BD_EXPANDABLE_WALL ||
9760                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9761                              element == EL_EXPANDABLE_STEELWALL_ANY);
9762
9763   boolean stop_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9764                              element == EL_EXPANDABLE_STEELWALL_VERTICAL);
9765
9766   boolean stop_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9767                              element == EL_EXPANDABLE_WALL ||
9768                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL);
9769
9770   int wall_growing = (is_steelwall ?
9771                       EL_EXPANDABLE_STEELWALL_GROWING :
9772                       EL_EXPANDABLE_WALL_GROWING);
9773
9774   int gfx_wall_growing_up    = (is_steelwall ?
9775                                 IMG_EXPANDABLE_STEELWALL_GROWING_UP :
9776                                 IMG_EXPANDABLE_WALL_GROWING_UP);
9777   int gfx_wall_growing_down  = (is_steelwall ?
9778                                 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN :
9779                                 IMG_EXPANDABLE_WALL_GROWING_DOWN);
9780   int gfx_wall_growing_left  = (is_steelwall ?
9781                                 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT :
9782                                 IMG_EXPANDABLE_WALL_GROWING_LEFT);
9783   int gfx_wall_growing_right = (is_steelwall ?
9784                                 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT :
9785                                 IMG_EXPANDABLE_WALL_GROWING_RIGHT);
9786
9787   if (IS_ANIMATED(graphic))
9788     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9789
9790   if (!MovDelay[ax][ay])        // start building new wall
9791     MovDelay[ax][ay] = 6;
9792
9793   if (MovDelay[ax][ay])         // wait some time before building new wall
9794   {
9795     MovDelay[ax][ay]--;
9796     if (MovDelay[ax][ay])
9797       return;
9798   }
9799
9800   if (IN_LEV_FIELD(ax, ay - 1) && IS_FREE(ax, ay - 1))
9801     free_top = TRUE;
9802   if (IN_LEV_FIELD(ax, ay + 1) && IS_FREE(ax, ay + 1))
9803     free_bottom = TRUE;
9804   if (IN_LEV_FIELD(ax - 1, ay) && IS_FREE(ax - 1, ay))
9805     free_left = TRUE;
9806   if (IN_LEV_FIELD(ax + 1, ay) && IS_FREE(ax + 1, ay))
9807     free_right = TRUE;
9808
9809   if (grow_vertical)
9810   {
9811     if (free_top)
9812     {
9813       Tile[ax][ay - 1] = wall_growing;
9814       Store[ax][ay - 1] = element;
9815       GfxDir[ax][ay - 1] = MovDir[ax][ay - 1] = MV_UP;
9816
9817       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay - 1)))
9818         DrawLevelGraphic(ax, ay - 1, gfx_wall_growing_up, 0);
9819
9820       new_wall = TRUE;
9821     }
9822
9823     if (free_bottom)
9824     {
9825       Tile[ax][ay + 1] = wall_growing;
9826       Store[ax][ay + 1] = element;
9827       GfxDir[ax][ay + 1] = MovDir[ax][ay + 1] = MV_DOWN;
9828
9829       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay + 1)))
9830         DrawLevelGraphic(ax, ay + 1, gfx_wall_growing_down, 0);
9831
9832       new_wall = TRUE;
9833     }
9834   }
9835
9836   if (grow_horizontal)
9837   {
9838     if (free_left)
9839     {
9840       Tile[ax - 1][ay] = wall_growing;
9841       Store[ax - 1][ay] = element;
9842       GfxDir[ax - 1][ay] = MovDir[ax - 1][ay] = MV_LEFT;
9843
9844       if (IN_SCR_FIELD(SCREENX(ax - 1), SCREENY(ay)))
9845         DrawLevelGraphic(ax - 1, ay, gfx_wall_growing_left, 0);
9846
9847       new_wall = TRUE;
9848     }
9849
9850     if (free_right)
9851     {
9852       Tile[ax + 1][ay] = wall_growing;
9853       Store[ax + 1][ay] = element;
9854       GfxDir[ax + 1][ay] = MovDir[ax + 1][ay] = MV_RIGHT;
9855
9856       if (IN_SCR_FIELD(SCREENX(ax + 1), SCREENY(ay)))
9857         DrawLevelGraphic(ax + 1, ay, gfx_wall_growing_right, 0);
9858
9859       new_wall = TRUE;
9860     }
9861   }
9862
9863   if (element == EL_EXPANDABLE_WALL && (free_left || free_right))
9864     TEST_DrawLevelField(ax, ay);
9865
9866   if (!IN_LEV_FIELD(ax, ay - 1) || IS_WALL(Tile[ax][ay - 1]))
9867     stop_top = TRUE;
9868   if (!IN_LEV_FIELD(ax, ay + 1) || IS_WALL(Tile[ax][ay + 1]))
9869     stop_bottom = TRUE;
9870   if (!IN_LEV_FIELD(ax - 1, ay) || IS_WALL(Tile[ax - 1][ay]))
9871     stop_left = TRUE;
9872   if (!IN_LEV_FIELD(ax + 1, ay) || IS_WALL(Tile[ax + 1][ay]))
9873     stop_right = TRUE;
9874
9875   if (((stop_top && stop_bottom) || stop_horizontal) &&
9876       ((stop_left && stop_right) || stop_vertical))
9877     Tile[ax][ay] = (is_steelwall ? EL_STEELWALL : EL_WALL);
9878
9879   if (new_wall)
9880     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9881 }
9882
9883 static void CheckForDragon(int x, int y)
9884 {
9885   int i, j;
9886   boolean dragon_found = FALSE;
9887   struct XY *xy = xy_topdown;
9888
9889   for (i = 0; i < NUM_DIRECTIONS; i++)
9890   {
9891     for (j = 0; j < 4; j++)
9892     {
9893       int xx = x + j * xy[i].x;
9894       int yy = y + j * xy[i].y;
9895
9896       if (IN_LEV_FIELD(xx, yy) &&
9897           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9898       {
9899         if (Tile[xx][yy] == EL_DRAGON)
9900           dragon_found = TRUE;
9901       }
9902       else
9903         break;
9904     }
9905   }
9906
9907   if (!dragon_found)
9908   {
9909     for (i = 0; i < NUM_DIRECTIONS; i++)
9910     {
9911       for (j = 0; j < 3; j++)
9912       {
9913         int xx = x + j * xy[i].x;
9914         int yy = y + j * xy[i].y;
9915
9916         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9917         {
9918           Tile[xx][yy] = EL_EMPTY;
9919           TEST_DrawLevelField(xx, yy);
9920         }
9921         else
9922           break;
9923       }
9924     }
9925   }
9926 }
9927
9928 static void InitBuggyBase(int x, int y)
9929 {
9930   int element = Tile[x][y];
9931   int activating_delay = FRAMES_PER_SECOND / 4;
9932
9933   ChangeDelay[x][y] =
9934     (element == EL_SP_BUGGY_BASE ?
9935      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9936      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9937      activating_delay :
9938      element == EL_SP_BUGGY_BASE_ACTIVE ?
9939      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9940 }
9941
9942 static void WarnBuggyBase(int x, int y)
9943 {
9944   int i;
9945   struct XY *xy = xy_topdown;
9946
9947   for (i = 0; i < NUM_DIRECTIONS; i++)
9948   {
9949     int xx = x + xy[i].x;
9950     int yy = y + xy[i].y;
9951
9952     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9953     {
9954       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9955
9956       break;
9957     }
9958   }
9959 }
9960
9961 static void InitTrap(int x, int y)
9962 {
9963   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9964 }
9965
9966 static void ActivateTrap(int x, int y)
9967 {
9968   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9969 }
9970
9971 static void ChangeActiveTrap(int x, int y)
9972 {
9973   int graphic = IMG_TRAP_ACTIVE;
9974
9975   // if new animation frame was drawn, correct crumbled sand border
9976   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9977     TEST_DrawLevelFieldCrumbled(x, y);
9978 }
9979
9980 static int getSpecialActionElement(int element, int number, int base_element)
9981 {
9982   return (element != EL_EMPTY ? element :
9983           number != -1 ? base_element + number - 1 :
9984           EL_EMPTY);
9985 }
9986
9987 static int getModifiedActionNumber(int value_old, int operator, int operand,
9988                                    int value_min, int value_max)
9989 {
9990   int value_new = (operator == CA_MODE_SET      ? operand :
9991                    operator == CA_MODE_ADD      ? value_old + operand :
9992                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9993                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9994                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9995                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9996                    value_old);
9997
9998   return (value_new < value_min ? value_min :
9999           value_new > value_max ? value_max :
10000           value_new);
10001 }
10002
10003 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10004 {
10005   struct ElementInfo *ei = &element_info[element];
10006   struct ElementChangeInfo *change = &ei->change_page[page];
10007   int target_element = change->target_element;
10008   int action_type = change->action_type;
10009   int action_mode = change->action_mode;
10010   int action_arg = change->action_arg;
10011   int action_element = change->action_element;
10012   int i;
10013
10014   if (!change->has_action)
10015     return;
10016
10017   // ---------- determine action paramater values -----------------------------
10018
10019   int level_time_value =
10020     (level.time > 0 ? TimeLeft :
10021      TimePlayed);
10022
10023   int action_arg_element_raw =
10024     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10025      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10026      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10027      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10028      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10029      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10030      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10031      EL_EMPTY);
10032   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10033
10034   int action_arg_direction =
10035     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10036      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10037      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10038      change->actual_trigger_side :
10039      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10040      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10041      MV_NONE);
10042
10043   int action_arg_number_min =
10044     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10045      CA_ARG_MIN);
10046
10047   int action_arg_number_max =
10048     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10049      action_type == CA_SET_LEVEL_GEMS ? 999 :
10050      action_type == CA_SET_LEVEL_TIME ? 9999 :
10051      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10052      action_type == CA_SET_CE_VALUE ? 9999 :
10053      action_type == CA_SET_CE_SCORE ? 9999 :
10054      CA_ARG_MAX);
10055
10056   int action_arg_number_reset =
10057     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10058      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10059      action_type == CA_SET_LEVEL_TIME ? level.time :
10060      action_type == CA_SET_LEVEL_SCORE ? 0 :
10061      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10062      action_type == CA_SET_CE_SCORE ? 0 :
10063      0);
10064
10065   int action_arg_number =
10066     (action_arg <= CA_ARG_MAX ? action_arg :
10067      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10068      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10069      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10070      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10071      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10072      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10073      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10074      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10075      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10076      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10077      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10078      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10079      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10080      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10081      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10082      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10083      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10084      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10085      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10086      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10087      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10088      -1);
10089
10090   int action_arg_number_old =
10091     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10092      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10093      action_type == CA_SET_LEVEL_SCORE ? game.score :
10094      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10095      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10096      0);
10097
10098   int action_arg_number_new =
10099     getModifiedActionNumber(action_arg_number_old,
10100                             action_mode, action_arg_number,
10101                             action_arg_number_min, action_arg_number_max);
10102
10103   int trigger_player_bits =
10104     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10105      change->actual_trigger_player_bits : change->trigger_player);
10106
10107   int action_arg_player_bits =
10108     (action_arg >= CA_ARG_PLAYER_1 &&
10109      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10110      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10111      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10112      PLAYER_BITS_ANY);
10113
10114   // ---------- execute action  -----------------------------------------------
10115
10116   switch (action_type)
10117   {
10118     case CA_NO_ACTION:
10119     {
10120       return;
10121     }
10122
10123     // ---------- level actions  ----------------------------------------------
10124
10125     case CA_RESTART_LEVEL:
10126     {
10127       game.restart_level = TRUE;
10128
10129       break;
10130     }
10131
10132     case CA_SHOW_ENVELOPE:
10133     {
10134       int element = getSpecialActionElement(action_arg_element,
10135                                             action_arg_number, EL_ENVELOPE_1);
10136
10137       if (IS_ENVELOPE(element))
10138         local_player->show_envelope = element;
10139
10140       break;
10141     }
10142
10143     case CA_SET_LEVEL_TIME:
10144     {
10145       if (level.time > 0)       // only modify limited time value
10146       {
10147         TimeLeft = action_arg_number_new;
10148
10149         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10150
10151         DisplayGameControlValues();
10152
10153         if (!TimeLeft && game.time_limit)
10154           for (i = 0; i < MAX_PLAYERS; i++)
10155             KillPlayer(&stored_player[i]);
10156       }
10157
10158       break;
10159     }
10160
10161     case CA_SET_LEVEL_SCORE:
10162     {
10163       game.score = action_arg_number_new;
10164
10165       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10166
10167       DisplayGameControlValues();
10168
10169       break;
10170     }
10171
10172     case CA_SET_LEVEL_GEMS:
10173     {
10174       game.gems_still_needed = action_arg_number_new;
10175
10176       game.snapshot.collected_item = TRUE;
10177
10178       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10179
10180       DisplayGameControlValues();
10181
10182       break;
10183     }
10184
10185     case CA_SET_LEVEL_WIND:
10186     {
10187       game.wind_direction = action_arg_direction;
10188
10189       break;
10190     }
10191
10192     case CA_SET_LEVEL_RANDOM_SEED:
10193     {
10194       // ensure that setting a new random seed while playing is predictable
10195       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10196
10197       break;
10198     }
10199
10200     // ---------- player actions  ---------------------------------------------
10201
10202     case CA_MOVE_PLAYER:
10203     case CA_MOVE_PLAYER_NEW:
10204     {
10205       // automatically move to the next field in specified direction
10206       for (i = 0; i < MAX_PLAYERS; i++)
10207         if (trigger_player_bits & (1 << i))
10208           if (action_type == CA_MOVE_PLAYER ||
10209               stored_player[i].MovPos == 0)
10210             stored_player[i].programmed_action = action_arg_direction;
10211
10212       break;
10213     }
10214
10215     case CA_EXIT_PLAYER:
10216     {
10217       for (i = 0; i < MAX_PLAYERS; i++)
10218         if (action_arg_player_bits & (1 << i))
10219           ExitPlayer(&stored_player[i]);
10220
10221       if (game.players_still_needed == 0)
10222         LevelSolved();
10223
10224       break;
10225     }
10226
10227     case CA_KILL_PLAYER:
10228     {
10229       for (i = 0; i < MAX_PLAYERS; i++)
10230         if (action_arg_player_bits & (1 << i))
10231           KillPlayer(&stored_player[i]);
10232
10233       break;
10234     }
10235
10236     case CA_SET_PLAYER_KEYS:
10237     {
10238       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10239       int element = getSpecialActionElement(action_arg_element,
10240                                             action_arg_number, EL_KEY_1);
10241
10242       if (IS_KEY(element))
10243       {
10244         for (i = 0; i < MAX_PLAYERS; i++)
10245         {
10246           if (trigger_player_bits & (1 << i))
10247           {
10248             stored_player[i].key[KEY_NR(element)] = key_state;
10249
10250             DrawGameDoorValues();
10251           }
10252         }
10253       }
10254
10255       break;
10256     }
10257
10258     case CA_SET_PLAYER_SPEED:
10259     {
10260       for (i = 0; i < MAX_PLAYERS; i++)
10261       {
10262         if (trigger_player_bits & (1 << i))
10263         {
10264           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10265
10266           if (action_arg == CA_ARG_SPEED_FASTER &&
10267               stored_player[i].cannot_move)
10268           {
10269             action_arg_number = STEPSIZE_VERY_SLOW;
10270           }
10271           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10272                    action_arg == CA_ARG_SPEED_FASTER)
10273           {
10274             action_arg_number = 2;
10275             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10276                            CA_MODE_MULTIPLY);
10277           }
10278           else if (action_arg == CA_ARG_NUMBER_RESET)
10279           {
10280             action_arg_number = level.initial_player_stepsize[i];
10281           }
10282
10283           move_stepsize =
10284             getModifiedActionNumber(move_stepsize,
10285                                     action_mode,
10286                                     action_arg_number,
10287                                     action_arg_number_min,
10288                                     action_arg_number_max);
10289
10290           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10291         }
10292       }
10293
10294       break;
10295     }
10296
10297     case CA_SET_PLAYER_SHIELD:
10298     {
10299       for (i = 0; i < MAX_PLAYERS; i++)
10300       {
10301         if (trigger_player_bits & (1 << i))
10302         {
10303           if (action_arg == CA_ARG_SHIELD_OFF)
10304           {
10305             stored_player[i].shield_normal_time_left = 0;
10306             stored_player[i].shield_deadly_time_left = 0;
10307           }
10308           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10309           {
10310             stored_player[i].shield_normal_time_left = 999999;
10311           }
10312           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10313           {
10314             stored_player[i].shield_normal_time_left = 999999;
10315             stored_player[i].shield_deadly_time_left = 999999;
10316           }
10317         }
10318       }
10319
10320       break;
10321     }
10322
10323     case CA_SET_PLAYER_GRAVITY:
10324     {
10325       for (i = 0; i < MAX_PLAYERS; i++)
10326       {
10327         if (trigger_player_bits & (1 << i))
10328         {
10329           stored_player[i].gravity =
10330             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10331              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10332              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10333              stored_player[i].gravity);
10334         }
10335       }
10336
10337       break;
10338     }
10339
10340     case CA_SET_PLAYER_ARTWORK:
10341     {
10342       for (i = 0; i < MAX_PLAYERS; i++)
10343       {
10344         if (trigger_player_bits & (1 << i))
10345         {
10346           int artwork_element = action_arg_element;
10347
10348           if (action_arg == CA_ARG_ELEMENT_RESET)
10349             artwork_element =
10350               (level.use_artwork_element[i] ? level.artwork_element[i] :
10351                stored_player[i].element_nr);
10352
10353           if (stored_player[i].artwork_element != artwork_element)
10354             stored_player[i].Frame = 0;
10355
10356           stored_player[i].artwork_element = artwork_element;
10357
10358           SetPlayerWaiting(&stored_player[i], FALSE);
10359
10360           // set number of special actions for bored and sleeping animation
10361           stored_player[i].num_special_action_bored =
10362             get_num_special_action(artwork_element,
10363                                    ACTION_BORING_1, ACTION_BORING_LAST);
10364           stored_player[i].num_special_action_sleeping =
10365             get_num_special_action(artwork_element,
10366                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10367         }
10368       }
10369
10370       break;
10371     }
10372
10373     case CA_SET_PLAYER_INVENTORY:
10374     {
10375       for (i = 0; i < MAX_PLAYERS; i++)
10376       {
10377         struct PlayerInfo *player = &stored_player[i];
10378         int j, k;
10379
10380         if (trigger_player_bits & (1 << i))
10381         {
10382           int inventory_element = action_arg_element;
10383
10384           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10385               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10386               action_arg == CA_ARG_ELEMENT_ACTION)
10387           {
10388             int element = inventory_element;
10389             int collect_count = element_info[element].collect_count_initial;
10390
10391             if (!IS_CUSTOM_ELEMENT(element))
10392               collect_count = 1;
10393
10394             if (collect_count == 0)
10395               player->inventory_infinite_element = element;
10396             else
10397               for (k = 0; k < collect_count; k++)
10398                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10399                   player->inventory_element[player->inventory_size++] =
10400                     element;
10401           }
10402           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10403                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10404                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10405           {
10406             if (player->inventory_infinite_element != EL_UNDEFINED &&
10407                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10408                                      action_arg_element_raw))
10409               player->inventory_infinite_element = EL_UNDEFINED;
10410
10411             for (k = 0, j = 0; j < player->inventory_size; j++)
10412             {
10413               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10414                                         action_arg_element_raw))
10415                 player->inventory_element[k++] = player->inventory_element[j];
10416             }
10417
10418             player->inventory_size = k;
10419           }
10420           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10421           {
10422             if (player->inventory_size > 0)
10423             {
10424               for (j = 0; j < player->inventory_size - 1; j++)
10425                 player->inventory_element[j] = player->inventory_element[j + 1];
10426
10427               player->inventory_size--;
10428             }
10429           }
10430           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10431           {
10432             if (player->inventory_size > 0)
10433               player->inventory_size--;
10434           }
10435           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10436           {
10437             player->inventory_infinite_element = EL_UNDEFINED;
10438             player->inventory_size = 0;
10439           }
10440           else if (action_arg == CA_ARG_INVENTORY_RESET)
10441           {
10442             player->inventory_infinite_element = EL_UNDEFINED;
10443             player->inventory_size = 0;
10444
10445             if (level.use_initial_inventory[i])
10446             {
10447               for (j = 0; j < level.initial_inventory_size[i]; j++)
10448               {
10449                 int element = level.initial_inventory_content[i][j];
10450                 int collect_count = element_info[element].collect_count_initial;
10451
10452                 if (!IS_CUSTOM_ELEMENT(element))
10453                   collect_count = 1;
10454
10455                 if (collect_count == 0)
10456                   player->inventory_infinite_element = element;
10457                 else
10458                   for (k = 0; k < collect_count; k++)
10459                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10460                       player->inventory_element[player->inventory_size++] =
10461                         element;
10462               }
10463             }
10464           }
10465         }
10466       }
10467
10468       break;
10469     }
10470
10471     // ---------- CE actions  -------------------------------------------------
10472
10473     case CA_SET_CE_VALUE:
10474     {
10475       int last_ce_value = CustomValue[x][y];
10476
10477       CustomValue[x][y] = action_arg_number_new;
10478
10479       if (CustomValue[x][y] != last_ce_value)
10480       {
10481         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10482         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10483
10484         if (CustomValue[x][y] == 0)
10485         {
10486           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10487           ChangeCount[x][y] = 0;        // allow at least one more change
10488
10489           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10490           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10491         }
10492       }
10493
10494       break;
10495     }
10496
10497     case CA_SET_CE_SCORE:
10498     {
10499       int last_ce_score = ei->collect_score;
10500
10501       ei->collect_score = action_arg_number_new;
10502
10503       if (ei->collect_score != last_ce_score)
10504       {
10505         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10506         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10507
10508         if (ei->collect_score == 0)
10509         {
10510           int xx, yy;
10511
10512           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10513           ChangeCount[x][y] = 0;        // allow at least one more change
10514
10515           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10516           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10517
10518           /*
10519             This is a very special case that seems to be a mixture between
10520             CheckElementChange() and CheckTriggeredElementChange(): while
10521             the first one only affects single elements that are triggered
10522             directly, the second one affects multiple elements in the playfield
10523             that are triggered indirectly by another element. This is a third
10524             case: Changing the CE score always affects multiple identical CEs,
10525             so every affected CE must be checked, not only the single CE for
10526             which the CE score was changed in the first place (as every instance
10527             of that CE shares the same CE score, and therefore also can change)!
10528           */
10529           SCAN_PLAYFIELD(xx, yy)
10530           {
10531             if (Tile[xx][yy] == element)
10532               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10533                                  CE_SCORE_GETS_ZERO);
10534           }
10535         }
10536       }
10537
10538       break;
10539     }
10540
10541     case CA_SET_CE_ARTWORK:
10542     {
10543       int artwork_element = action_arg_element;
10544       boolean reset_frame = FALSE;
10545       int xx, yy;
10546
10547       if (action_arg == CA_ARG_ELEMENT_RESET)
10548         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10549                            element);
10550
10551       if (ei->gfx_element != artwork_element)
10552         reset_frame = TRUE;
10553
10554       ei->gfx_element = artwork_element;
10555
10556       SCAN_PLAYFIELD(xx, yy)
10557       {
10558         if (Tile[xx][yy] == element)
10559         {
10560           if (reset_frame)
10561           {
10562             ResetGfxAnimation(xx, yy);
10563             ResetRandomAnimationValue(xx, yy);
10564           }
10565
10566           TEST_DrawLevelField(xx, yy);
10567         }
10568       }
10569
10570       break;
10571     }
10572
10573     // ---------- engine actions  ---------------------------------------------
10574
10575     case CA_SET_ENGINE_SCAN_MODE:
10576     {
10577       InitPlayfieldScanMode(action_arg);
10578
10579       break;
10580     }
10581
10582     default:
10583       break;
10584   }
10585 }
10586
10587 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10588 {
10589   int old_element = Tile[x][y];
10590   int new_element = GetElementFromGroupElement(element);
10591   int previous_move_direction = MovDir[x][y];
10592   int last_ce_value = CustomValue[x][y];
10593   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10594   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10595   boolean add_player_onto_element = (new_element_is_player &&
10596                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10597                                      IS_WALKABLE(old_element));
10598
10599   if (!add_player_onto_element)
10600   {
10601     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10602       RemoveMovingField(x, y);
10603     else
10604       RemoveField(x, y);
10605
10606     Tile[x][y] = new_element;
10607
10608     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10609       MovDir[x][y] = previous_move_direction;
10610
10611     if (element_info[new_element].use_last_ce_value)
10612       CustomValue[x][y] = last_ce_value;
10613
10614     InitField_WithBug1(x, y, FALSE);
10615
10616     new_element = Tile[x][y];   // element may have changed
10617
10618     ResetGfxAnimation(x, y);
10619     ResetRandomAnimationValue(x, y);
10620
10621     TEST_DrawLevelField(x, y);
10622
10623     if (GFX_CRUMBLED(new_element))
10624       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10625   }
10626
10627   // check if element under the player changes from accessible to unaccessible
10628   // (needed for special case of dropping element which then changes)
10629   // (must be checked after creating new element for walkable group elements)
10630   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10631       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10632   {
10633     Bang(x, y);
10634
10635     return;
10636   }
10637
10638   // "ChangeCount" not set yet to allow "entered by player" change one time
10639   if (new_element_is_player)
10640     RelocatePlayer(x, y, new_element);
10641
10642   if (is_change)
10643     ChangeCount[x][y]++;        // count number of changes in the same frame
10644
10645   TestIfBadThingTouchesPlayer(x, y);
10646   TestIfPlayerTouchesCustomElement(x, y);
10647   TestIfElementTouchesCustomElement(x, y);
10648 }
10649
10650 static void CreateField(int x, int y, int element)
10651 {
10652   CreateFieldExt(x, y, element, FALSE);
10653 }
10654
10655 static void CreateElementFromChange(int x, int y, int element)
10656 {
10657   element = GET_VALID_RUNTIME_ELEMENT(element);
10658
10659   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10660   {
10661     int old_element = Tile[x][y];
10662
10663     // prevent changed element from moving in same engine frame
10664     // unless both old and new element can either fall or move
10665     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10666         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10667       Stop[x][y] = TRUE;
10668   }
10669
10670   CreateFieldExt(x, y, element, TRUE);
10671 }
10672
10673 static boolean ChangeElement(int x, int y, int element, int page)
10674 {
10675   struct ElementInfo *ei = &element_info[element];
10676   struct ElementChangeInfo *change = &ei->change_page[page];
10677   int ce_value = CustomValue[x][y];
10678   int ce_score = ei->collect_score;
10679   int target_element;
10680   int old_element = Tile[x][y];
10681
10682   // always use default change event to prevent running into a loop
10683   if (ChangeEvent[x][y] == -1)
10684     ChangeEvent[x][y] = CE_DELAY;
10685
10686   if (ChangeEvent[x][y] == CE_DELAY)
10687   {
10688     // reset actual trigger element, trigger player and action element
10689     change->actual_trigger_element = EL_EMPTY;
10690     change->actual_trigger_player = EL_EMPTY;
10691     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10692     change->actual_trigger_side = CH_SIDE_NONE;
10693     change->actual_trigger_ce_value = 0;
10694     change->actual_trigger_ce_score = 0;
10695   }
10696
10697   // do not change elements more than a specified maximum number of changes
10698   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10699     return FALSE;
10700
10701   ChangeCount[x][y]++;          // count number of changes in the same frame
10702
10703   if (change->explode)
10704   {
10705     Bang(x, y);
10706
10707     return TRUE;
10708   }
10709
10710   if (change->use_target_content)
10711   {
10712     boolean complete_replace = TRUE;
10713     boolean can_replace[3][3];
10714     int xx, yy;
10715
10716     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10717     {
10718       boolean is_empty;
10719       boolean is_walkable;
10720       boolean is_diggable;
10721       boolean is_collectible;
10722       boolean is_removable;
10723       boolean is_destructible;
10724       int ex = x + xx - 1;
10725       int ey = y + yy - 1;
10726       int content_element = change->target_content.e[xx][yy];
10727       int e;
10728
10729       can_replace[xx][yy] = TRUE;
10730
10731       if (ex == x && ey == y)   // do not check changing element itself
10732         continue;
10733
10734       if (content_element == EL_EMPTY_SPACE)
10735       {
10736         can_replace[xx][yy] = FALSE;    // do not replace border with space
10737
10738         continue;
10739       }
10740
10741       if (!IN_LEV_FIELD(ex, ey))
10742       {
10743         can_replace[xx][yy] = FALSE;
10744         complete_replace = FALSE;
10745
10746         continue;
10747       }
10748
10749       e = Tile[ex][ey];
10750
10751       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10752         e = MovingOrBlocked2Element(ex, ey);
10753
10754       is_empty = (IS_FREE(ex, ey) ||
10755                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10756
10757       is_walkable     = (is_empty || IS_WALKABLE(e));
10758       is_diggable     = (is_empty || IS_DIGGABLE(e));
10759       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10760       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10761       is_removable    = (is_diggable || is_collectible);
10762
10763       can_replace[xx][yy] =
10764         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10765           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10766           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10767           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10768           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10769           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10770          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10771
10772       if (!can_replace[xx][yy])
10773         complete_replace = FALSE;
10774     }
10775
10776     if (!change->only_if_complete || complete_replace)
10777     {
10778       boolean something_has_changed = FALSE;
10779
10780       if (change->only_if_complete && change->use_random_replace &&
10781           RND(100) < change->random_percentage)
10782         return FALSE;
10783
10784       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10785       {
10786         int ex = x + xx - 1;
10787         int ey = y + yy - 1;
10788         int content_element;
10789
10790         if (can_replace[xx][yy] && (!change->use_random_replace ||
10791                                     RND(100) < change->random_percentage))
10792         {
10793           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10794             RemoveMovingField(ex, ey);
10795
10796           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10797
10798           content_element = change->target_content.e[xx][yy];
10799           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10800                                               ce_value, ce_score);
10801
10802           CreateElementFromChange(ex, ey, target_element);
10803
10804           something_has_changed = TRUE;
10805
10806           // for symmetry reasons, freeze newly created border elements
10807           if (ex != x || ey != y)
10808             Stop[ex][ey] = TRUE;        // no more moving in this frame
10809         }
10810       }
10811
10812       if (something_has_changed)
10813       {
10814         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10815         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10816       }
10817     }
10818   }
10819   else
10820   {
10821     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10822                                         ce_value, ce_score);
10823
10824     if (element == EL_DIAGONAL_GROWING ||
10825         element == EL_DIAGONAL_SHRINKING)
10826     {
10827       target_element = Store[x][y];
10828
10829       Store[x][y] = EL_EMPTY;
10830     }
10831
10832     // special case: element changes to player (and may be kept if walkable)
10833     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
10834       CreateElementFromChange(x, y, EL_EMPTY);
10835
10836     CreateElementFromChange(x, y, target_element);
10837
10838     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10839     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10840   }
10841
10842   // this uses direct change before indirect change
10843   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10844
10845   return TRUE;
10846 }
10847
10848 static void HandleElementChange(int x, int y, int page)
10849 {
10850   int element = MovingOrBlocked2Element(x, y);
10851   struct ElementInfo *ei = &element_info[element];
10852   struct ElementChangeInfo *change = &ei->change_page[page];
10853   boolean handle_action_before_change = FALSE;
10854
10855 #ifdef DEBUG
10856   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10857       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10858   {
10859     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10860           x, y, element, element_info[element].token_name);
10861     Debug("game:playing:HandleElementChange", "This should never happen!");
10862   }
10863 #endif
10864
10865   // this can happen with classic bombs on walkable, changing elements
10866   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10867   {
10868     return;
10869   }
10870
10871   if (ChangeDelay[x][y] == 0)           // initialize element change
10872   {
10873     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10874
10875     if (change->can_change)
10876     {
10877       // !!! not clear why graphic animation should be reset at all here !!!
10878       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10879       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10880
10881       /*
10882         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10883
10884         When using an animation frame delay of 1 (this only happens with
10885         "sp_zonk.moving.left/right" in the classic graphics), the default
10886         (non-moving) animation shows wrong animation frames (while the
10887         moving animation, like "sp_zonk.moving.left/right", is correct,
10888         so this graphical bug never shows up with the classic graphics).
10889         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10890         be drawn instead of the correct frames 0,1,2,3. This is caused by
10891         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10892         an element change: First when the change delay ("ChangeDelay[][]")
10893         counter has reached zero after decrementing, then a second time in
10894         the next frame (after "GfxFrame[][]" was already incremented) when
10895         "ChangeDelay[][]" is reset to the initial delay value again.
10896
10897         This causes frame 0 to be drawn twice, while the last frame won't
10898         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10899
10900         As some animations may already be cleverly designed around this bug
10901         (at least the "Snake Bite" snake tail animation does this), it cannot
10902         simply be fixed here without breaking such existing animations.
10903         Unfortunately, it cannot easily be detected if a graphics set was
10904         designed "before" or "after" the bug was fixed. As a workaround,
10905         a new graphics set option "game.graphics_engine_version" was added
10906         to be able to specify the game's major release version for which the
10907         graphics set was designed, which can then be used to decide if the
10908         bugfix should be used (version 4 and above) or not (version 3 or
10909         below, or if no version was specified at all, as with old sets).
10910
10911         (The wrong/fixed animation frames can be tested with the test level set
10912         "test_gfxframe" and level "000", which contains a specially prepared
10913         custom element at level position (x/y) == (11/9) which uses the zonk
10914         animation mentioned above. Using "game.graphics_engine_version: 4"
10915         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10916         This can also be seen from the debug output for this test element.)
10917       */
10918
10919       // when a custom element is about to change (for example by change delay),
10920       // do not reset graphic animation when the custom element is moving
10921       if (game.graphics_engine_version < 4 &&
10922           !IS_MOVING(x, y))
10923       {
10924         ResetGfxAnimation(x, y);
10925         ResetRandomAnimationValue(x, y);
10926       }
10927
10928       if (change->pre_change_function)
10929         change->pre_change_function(x, y);
10930     }
10931   }
10932
10933   ChangeDelay[x][y]--;
10934
10935   if (ChangeDelay[x][y] != 0)           // continue element change
10936   {
10937     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10938
10939     // also needed if CE can not change, but has CE delay with CE action
10940     if (IS_ANIMATED(graphic))
10941       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10942
10943     if (change->can_change)
10944     {
10945       if (change->change_function)
10946         change->change_function(x, y);
10947     }
10948   }
10949   else                                  // finish element change
10950   {
10951     if (ChangePage[x][y] != -1)         // remember page from delayed change
10952     {
10953       page = ChangePage[x][y];
10954       ChangePage[x][y] = -1;
10955
10956       change = &ei->change_page[page];
10957     }
10958
10959     if (IS_MOVING(x, y))                // never change a running system ;-)
10960     {
10961       ChangeDelay[x][y] = 1;            // try change after next move step
10962       ChangePage[x][y] = page;          // remember page to use for change
10963
10964       return;
10965     }
10966
10967     // special case: set new level random seed before changing element
10968     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10969       handle_action_before_change = TRUE;
10970
10971     if (change->has_action && handle_action_before_change)
10972       ExecuteCustomElementAction(x, y, element, page);
10973
10974     if (change->can_change)
10975     {
10976       if (ChangeElement(x, y, element, page))
10977       {
10978         if (change->post_change_function)
10979           change->post_change_function(x, y);
10980       }
10981     }
10982
10983     if (change->has_action && !handle_action_before_change)
10984       ExecuteCustomElementAction(x, y, element, page);
10985   }
10986 }
10987
10988 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10989                                               int trigger_element,
10990                                               int trigger_event,
10991                                               int trigger_player,
10992                                               int trigger_side,
10993                                               int trigger_page)
10994 {
10995   boolean change_done_any = FALSE;
10996   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10997   int i;
10998
10999   if (!(trigger_events[trigger_element][trigger_event]))
11000     return FALSE;
11001
11002   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11003
11004   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11005   {
11006     int element = EL_CUSTOM_START + i;
11007     boolean change_done = FALSE;
11008     int p;
11009
11010     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11011         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11012       continue;
11013
11014     for (p = 0; p < element_info[element].num_change_pages; p++)
11015     {
11016       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11017
11018       if (change->can_change_or_has_action &&
11019           change->has_event[trigger_event] &&
11020           change->trigger_side & trigger_side &&
11021           change->trigger_player & trigger_player &&
11022           change->trigger_page & trigger_page_bits &&
11023           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11024       {
11025         change->actual_trigger_element = trigger_element;
11026         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11027         change->actual_trigger_player_bits = trigger_player;
11028         change->actual_trigger_side = trigger_side;
11029         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11030         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11031
11032         if ((change->can_change && !change_done) || change->has_action)
11033         {
11034           int x, y;
11035
11036           SCAN_PLAYFIELD(x, y)
11037           {
11038             if (Tile[x][y] == element)
11039             {
11040               if (change->can_change && !change_done)
11041               {
11042                 // if element already changed in this frame, not only prevent
11043                 // another element change (checked in ChangeElement()), but
11044                 // also prevent additional element actions for this element
11045
11046                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11047                     !level.use_action_after_change_bug)
11048                   continue;
11049
11050                 ChangeDelay[x][y] = 1;
11051                 ChangeEvent[x][y] = trigger_event;
11052
11053                 HandleElementChange(x, y, p);
11054               }
11055               else if (change->has_action)
11056               {
11057                 // if element already changed in this frame, not only prevent
11058                 // another element change (checked in ChangeElement()), but
11059                 // also prevent additional element actions for this element
11060
11061                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11062                     !level.use_action_after_change_bug)
11063                   continue;
11064
11065                 ExecuteCustomElementAction(x, y, element, p);
11066                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11067               }
11068             }
11069           }
11070
11071           if (change->can_change)
11072           {
11073             change_done = TRUE;
11074             change_done_any = TRUE;
11075           }
11076         }
11077       }
11078     }
11079   }
11080
11081   RECURSION_LOOP_DETECTION_END();
11082
11083   return change_done_any;
11084 }
11085
11086 static boolean CheckElementChangeExt(int x, int y,
11087                                      int element,
11088                                      int trigger_element,
11089                                      int trigger_event,
11090                                      int trigger_player,
11091                                      int trigger_side)
11092 {
11093   boolean change_done = FALSE;
11094   int p;
11095
11096   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11097       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11098     return FALSE;
11099
11100   if (Tile[x][y] == EL_BLOCKED)
11101   {
11102     Blocked2Moving(x, y, &x, &y);
11103     element = Tile[x][y];
11104   }
11105
11106   // check if element has already changed or is about to change after moving
11107   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11108        Tile[x][y] != element) ||
11109
11110       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11111        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11112         ChangePage[x][y] != -1)))
11113     return FALSE;
11114
11115   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11116
11117   for (p = 0; p < element_info[element].num_change_pages; p++)
11118   {
11119     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11120
11121     /* check trigger element for all events where the element that is checked
11122        for changing interacts with a directly adjacent element -- this is
11123        different to element changes that affect other elements to change on the
11124        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11125     boolean check_trigger_element =
11126       (trigger_event == CE_NEXT_TO_X ||
11127        trigger_event == CE_TOUCHING_X ||
11128        trigger_event == CE_HITTING_X ||
11129        trigger_event == CE_HIT_BY_X ||
11130        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11131
11132     if (change->can_change_or_has_action &&
11133         change->has_event[trigger_event] &&
11134         change->trigger_side & trigger_side &&
11135         change->trigger_player & trigger_player &&
11136         (!check_trigger_element ||
11137          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11138     {
11139       change->actual_trigger_element = trigger_element;
11140       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11141       change->actual_trigger_player_bits = trigger_player;
11142       change->actual_trigger_side = trigger_side;
11143       change->actual_trigger_ce_value = CustomValue[x][y];
11144       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11145
11146       // special case: trigger element not at (x,y) position for some events
11147       if (check_trigger_element)
11148       {
11149         static struct
11150         {
11151           int dx, dy;
11152         } move_xy[] =
11153           {
11154             {  0,  0 },
11155             { -1,  0 },
11156             { +1,  0 },
11157             {  0,  0 },
11158             {  0, -1 },
11159             {  0,  0 }, { 0, 0 }, { 0, 0 },
11160             {  0, +1 }
11161           };
11162
11163         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11164         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11165
11166         change->actual_trigger_ce_value = CustomValue[xx][yy];
11167         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11168       }
11169
11170       if (change->can_change && !change_done)
11171       {
11172         ChangeDelay[x][y] = 1;
11173         ChangeEvent[x][y] = trigger_event;
11174
11175         HandleElementChange(x, y, p);
11176
11177         change_done = TRUE;
11178       }
11179       else if (change->has_action)
11180       {
11181         ExecuteCustomElementAction(x, y, element, p);
11182         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11183       }
11184     }
11185   }
11186
11187   RECURSION_LOOP_DETECTION_END();
11188
11189   return change_done;
11190 }
11191
11192 static void PlayPlayerSound(struct PlayerInfo *player)
11193 {
11194   int jx = player->jx, jy = player->jy;
11195   int sound_element = player->artwork_element;
11196   int last_action = player->last_action_waiting;
11197   int action = player->action_waiting;
11198
11199   if (player->is_waiting)
11200   {
11201     if (action != last_action)
11202       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11203     else
11204       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11205   }
11206   else
11207   {
11208     if (action != last_action)
11209       StopSound(element_info[sound_element].sound[last_action]);
11210
11211     if (last_action == ACTION_SLEEPING)
11212       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11213   }
11214 }
11215
11216 static void PlayAllPlayersSound(void)
11217 {
11218   int i;
11219
11220   for (i = 0; i < MAX_PLAYERS; i++)
11221     if (stored_player[i].active)
11222       PlayPlayerSound(&stored_player[i]);
11223 }
11224
11225 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11226 {
11227   boolean last_waiting = player->is_waiting;
11228   int move_dir = player->MovDir;
11229
11230   player->dir_waiting = move_dir;
11231   player->last_action_waiting = player->action_waiting;
11232
11233   if (is_waiting)
11234   {
11235     if (!last_waiting)          // not waiting -> waiting
11236     {
11237       player->is_waiting = TRUE;
11238
11239       player->frame_counter_bored =
11240         FrameCounter +
11241         game.player_boring_delay_fixed +
11242         GetSimpleRandom(game.player_boring_delay_random);
11243       player->frame_counter_sleeping =
11244         FrameCounter +
11245         game.player_sleeping_delay_fixed +
11246         GetSimpleRandom(game.player_sleeping_delay_random);
11247
11248       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11249     }
11250
11251     if (game.player_sleeping_delay_fixed +
11252         game.player_sleeping_delay_random > 0 &&
11253         player->anim_delay_counter == 0 &&
11254         player->post_delay_counter == 0 &&
11255         FrameCounter >= player->frame_counter_sleeping)
11256       player->is_sleeping = TRUE;
11257     else if (game.player_boring_delay_fixed +
11258              game.player_boring_delay_random > 0 &&
11259              FrameCounter >= player->frame_counter_bored)
11260       player->is_bored = TRUE;
11261
11262     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11263                               player->is_bored ? ACTION_BORING :
11264                               ACTION_WAITING);
11265
11266     if (player->is_sleeping && player->use_murphy)
11267     {
11268       // special case for sleeping Murphy when leaning against non-free tile
11269
11270       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11271           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11272            !IS_MOVING(player->jx - 1, player->jy)))
11273         move_dir = MV_LEFT;
11274       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11275                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11276                 !IS_MOVING(player->jx + 1, player->jy)))
11277         move_dir = MV_RIGHT;
11278       else
11279         player->is_sleeping = FALSE;
11280
11281       player->dir_waiting = move_dir;
11282     }
11283
11284     if (player->is_sleeping)
11285     {
11286       if (player->num_special_action_sleeping > 0)
11287       {
11288         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11289         {
11290           int last_special_action = player->special_action_sleeping;
11291           int num_special_action = player->num_special_action_sleeping;
11292           int special_action =
11293             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11294              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11295              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11296              last_special_action + 1 : ACTION_SLEEPING);
11297           int special_graphic =
11298             el_act_dir2img(player->artwork_element, special_action, move_dir);
11299
11300           player->anim_delay_counter =
11301             graphic_info[special_graphic].anim_delay_fixed +
11302             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11303           player->post_delay_counter =
11304             graphic_info[special_graphic].post_delay_fixed +
11305             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11306
11307           player->special_action_sleeping = special_action;
11308         }
11309
11310         if (player->anim_delay_counter > 0)
11311         {
11312           player->action_waiting = player->special_action_sleeping;
11313           player->anim_delay_counter--;
11314         }
11315         else if (player->post_delay_counter > 0)
11316         {
11317           player->post_delay_counter--;
11318         }
11319       }
11320     }
11321     else if (player->is_bored)
11322     {
11323       if (player->num_special_action_bored > 0)
11324       {
11325         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11326         {
11327           int special_action =
11328             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11329           int special_graphic =
11330             el_act_dir2img(player->artwork_element, special_action, move_dir);
11331
11332           player->anim_delay_counter =
11333             graphic_info[special_graphic].anim_delay_fixed +
11334             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11335           player->post_delay_counter =
11336             graphic_info[special_graphic].post_delay_fixed +
11337             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11338
11339           player->special_action_bored = special_action;
11340         }
11341
11342         if (player->anim_delay_counter > 0)
11343         {
11344           player->action_waiting = player->special_action_bored;
11345           player->anim_delay_counter--;
11346         }
11347         else if (player->post_delay_counter > 0)
11348         {
11349           player->post_delay_counter--;
11350         }
11351       }
11352     }
11353   }
11354   else if (last_waiting)        // waiting -> not waiting
11355   {
11356     player->is_waiting = FALSE;
11357     player->is_bored = FALSE;
11358     player->is_sleeping = FALSE;
11359
11360     player->frame_counter_bored = -1;
11361     player->frame_counter_sleeping = -1;
11362
11363     player->anim_delay_counter = 0;
11364     player->post_delay_counter = 0;
11365
11366     player->dir_waiting = player->MovDir;
11367     player->action_waiting = ACTION_DEFAULT;
11368
11369     player->special_action_bored = ACTION_DEFAULT;
11370     player->special_action_sleeping = ACTION_DEFAULT;
11371   }
11372 }
11373
11374 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11375 {
11376   if ((!player->is_moving  && player->was_moving) ||
11377       (player->MovPos == 0 && player->was_moving) ||
11378       (player->is_snapping && !player->was_snapping) ||
11379       (player->is_dropping && !player->was_dropping))
11380   {
11381     if (!CheckSaveEngineSnapshotToList())
11382       return;
11383
11384     player->was_moving = FALSE;
11385     player->was_snapping = TRUE;
11386     player->was_dropping = TRUE;
11387   }
11388   else
11389   {
11390     if (player->is_moving)
11391       player->was_moving = TRUE;
11392
11393     if (!player->is_snapping)
11394       player->was_snapping = FALSE;
11395
11396     if (!player->is_dropping)
11397       player->was_dropping = FALSE;
11398   }
11399
11400   static struct MouseActionInfo mouse_action_last = { 0 };
11401   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11402   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11403
11404   if (new_released)
11405     CheckSaveEngineSnapshotToList();
11406
11407   mouse_action_last = mouse_action;
11408 }
11409
11410 static void CheckSingleStepMode(struct PlayerInfo *player)
11411 {
11412   if (tape.single_step && tape.recording && !tape.pausing)
11413   {
11414     // as it is called "single step mode", just return to pause mode when the
11415     // player stopped moving after one tile (or never starts moving at all)
11416     // (reverse logic needed here in case single step mode used in team mode)
11417     if (player->is_moving ||
11418         player->is_pushing ||
11419         player->is_dropping_pressed ||
11420         player->effective_mouse_action.button)
11421       game.enter_single_step_mode = FALSE;
11422   }
11423
11424   CheckSaveEngineSnapshot(player);
11425 }
11426
11427 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11428 {
11429   int left      = player_action & JOY_LEFT;
11430   int right     = player_action & JOY_RIGHT;
11431   int up        = player_action & JOY_UP;
11432   int down      = player_action & JOY_DOWN;
11433   int button1   = player_action & JOY_BUTTON_1;
11434   int button2   = player_action & JOY_BUTTON_2;
11435   int dx        = (left ? -1 : right ? 1 : 0);
11436   int dy        = (up   ? -1 : down  ? 1 : 0);
11437
11438   if (!player->active || tape.pausing)
11439     return 0;
11440
11441   if (player_action)
11442   {
11443     if (button1)
11444       SnapField(player, dx, dy);
11445     else
11446     {
11447       if (button2)
11448         DropElement(player);
11449
11450       MovePlayer(player, dx, dy);
11451     }
11452
11453     CheckSingleStepMode(player);
11454
11455     SetPlayerWaiting(player, FALSE);
11456
11457     return player_action;
11458   }
11459   else
11460   {
11461     // no actions for this player (no input at player's configured device)
11462
11463     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11464     SnapField(player, 0, 0);
11465     CheckGravityMovementWhenNotMoving(player);
11466
11467     if (player->MovPos == 0)
11468       SetPlayerWaiting(player, TRUE);
11469
11470     if (player->MovPos == 0)    // needed for tape.playing
11471       player->is_moving = FALSE;
11472
11473     player->is_dropping = FALSE;
11474     player->is_dropping_pressed = FALSE;
11475     player->drop_pressed_delay = 0;
11476
11477     CheckSingleStepMode(player);
11478
11479     return 0;
11480   }
11481 }
11482
11483 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11484                                          byte *tape_action)
11485 {
11486   if (!tape.use_mouse_actions)
11487     return;
11488
11489   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11490   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11491   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11492 }
11493
11494 static void SetTapeActionFromMouseAction(byte *tape_action,
11495                                          struct MouseActionInfo *mouse_action)
11496 {
11497   if (!tape.use_mouse_actions)
11498     return;
11499
11500   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11501   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11502   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11503 }
11504
11505 static void CheckLevelSolved(void)
11506 {
11507   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11508   {
11509     if (game_em.level_solved &&
11510         !game_em.game_over)                             // game won
11511     {
11512       LevelSolved();
11513
11514       game_em.game_over = TRUE;
11515
11516       game.all_players_gone = TRUE;
11517     }
11518
11519     if (game_em.game_over)                              // game lost
11520       game.all_players_gone = TRUE;
11521   }
11522   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11523   {
11524     if (game_sp.level_solved &&
11525         !game_sp.game_over)                             // game won
11526     {
11527       LevelSolved();
11528
11529       game_sp.game_over = TRUE;
11530
11531       game.all_players_gone = TRUE;
11532     }
11533
11534     if (game_sp.game_over)                              // game lost
11535       game.all_players_gone = TRUE;
11536   }
11537   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11538   {
11539     if (game_mm.level_solved &&
11540         !game_mm.game_over)                             // game won
11541     {
11542       LevelSolved();
11543
11544       game_mm.game_over = TRUE;
11545
11546       game.all_players_gone = TRUE;
11547     }
11548
11549     if (game_mm.game_over)                              // game lost
11550       game.all_players_gone = TRUE;
11551   }
11552 }
11553
11554 static void CheckLevelTime_StepCounter(void)
11555 {
11556   int i;
11557
11558   TimePlayed++;
11559
11560   if (TimeLeft > 0)
11561   {
11562     TimeLeft--;
11563
11564     if (TimeLeft <= 10 && game.time_limit && !game.LevelSolved)
11565       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11566
11567     game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11568
11569     DisplayGameControlValues();
11570
11571     if (!TimeLeft && game.time_limit && !game.LevelSolved)
11572       for (i = 0; i < MAX_PLAYERS; i++)
11573         KillPlayer(&stored_player[i]);
11574   }
11575   else if (game.no_level_time_limit && !game.all_players_gone)
11576   {
11577     game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11578
11579     DisplayGameControlValues();
11580   }
11581 }
11582
11583 static void CheckLevelTime(void)
11584 {
11585   int i;
11586
11587   if (TimeFrames >= FRAMES_PER_SECOND)
11588   {
11589     TimeFrames = 0;
11590     TapeTime++;
11591
11592     for (i = 0; i < MAX_PLAYERS; i++)
11593     {
11594       struct PlayerInfo *player = &stored_player[i];
11595
11596       if (SHIELD_ON(player))
11597       {
11598         player->shield_normal_time_left--;
11599
11600         if (player->shield_deadly_time_left > 0)
11601           player->shield_deadly_time_left--;
11602       }
11603     }
11604
11605     if (!game.LevelSolved && !level.use_step_counter)
11606     {
11607       TimePlayed++;
11608
11609       if (TimeLeft > 0)
11610       {
11611         TimeLeft--;
11612
11613         if (TimeLeft <= 10 && game.time_limit)
11614           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11615
11616         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11617            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11618
11619         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11620
11621         if (!TimeLeft && game.time_limit)
11622         {
11623           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11624             game_em.lev->killed_out_of_time = TRUE;
11625           else
11626             for (i = 0; i < MAX_PLAYERS; i++)
11627               KillPlayer(&stored_player[i]);
11628         }
11629       }
11630       else if (game.no_level_time_limit && !game.all_players_gone)
11631       {
11632         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11633       }
11634
11635       game_em.lev->time = (game.no_level_time_limit ? TimePlayed : TimeLeft);
11636     }
11637
11638     if (tape.recording || tape.playing)
11639       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11640   }
11641
11642   if (tape.recording || tape.playing)
11643     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11644
11645   UpdateAndDisplayGameControlValues();
11646 }
11647
11648 void AdvanceFrameAndPlayerCounters(int player_nr)
11649 {
11650   int i;
11651
11652   // advance frame counters (global frame counter and time frame counter)
11653   FrameCounter++;
11654   TimeFrames++;
11655
11656   // advance player counters (counters for move delay, move animation etc.)
11657   for (i = 0; i < MAX_PLAYERS; i++)
11658   {
11659     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11660     int move_delay_value = stored_player[i].move_delay_value;
11661     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11662
11663     if (!advance_player_counters)       // not all players may be affected
11664       continue;
11665
11666     if (move_frames == 0)       // less than one move per game frame
11667     {
11668       int stepsize = TILEX / move_delay_value;
11669       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11670       int count = (stored_player[i].is_moving ?
11671                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11672
11673       if (count % delay == 0)
11674         move_frames = 1;
11675     }
11676
11677     stored_player[i].Frame += move_frames;
11678
11679     if (stored_player[i].MovPos != 0)
11680       stored_player[i].StepFrame += move_frames;
11681
11682     if (stored_player[i].move_delay > 0)
11683       stored_player[i].move_delay--;
11684
11685     // due to bugs in previous versions, counter must count up, not down
11686     if (stored_player[i].push_delay != -1)
11687       stored_player[i].push_delay++;
11688
11689     if (stored_player[i].drop_delay > 0)
11690       stored_player[i].drop_delay--;
11691
11692     if (stored_player[i].is_dropping_pressed)
11693       stored_player[i].drop_pressed_delay++;
11694   }
11695 }
11696
11697 void AdvanceFrameCounter(void)
11698 {
11699   FrameCounter++;
11700 }
11701
11702 void AdvanceGfxFrame(void)
11703 {
11704   int x, y;
11705
11706   SCAN_PLAYFIELD(x, y)
11707   {
11708     GfxFrame[x][y]++;
11709   }
11710 }
11711
11712 void StartGameActions(boolean init_network_game, boolean record_tape,
11713                       int random_seed)
11714 {
11715   unsigned int new_random_seed = InitRND(random_seed);
11716
11717   if (record_tape)
11718     TapeStartRecording(new_random_seed);
11719
11720   if (setup.auto_pause_on_start && !tape.pausing)
11721     TapeTogglePause(TAPE_TOGGLE_MANUAL);
11722
11723   if (init_network_game)
11724   {
11725     SendToServer_LevelFile();
11726     SendToServer_StartPlaying();
11727
11728     return;
11729   }
11730
11731   InitGame();
11732 }
11733
11734 static void GameActionsExt(void)
11735 {
11736 #if 0
11737   static unsigned int game_frame_delay = 0;
11738 #endif
11739   unsigned int game_frame_delay_value;
11740   byte *recorded_player_action;
11741   byte summarized_player_action = 0;
11742   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11743   int i;
11744
11745   // detect endless loops, caused by custom element programming
11746   if (recursion_loop_detected && recursion_loop_depth == 0)
11747   {
11748     char *message = getStringCat3("Internal Error! Element ",
11749                                   EL_NAME(recursion_loop_element),
11750                                   " caused endless loop! Quit the game?");
11751
11752     Warn("element '%s' caused endless loop in game engine",
11753          EL_NAME(recursion_loop_element));
11754
11755     RequestQuitGameExt(program.headless, level_editor_test_game, message);
11756
11757     recursion_loop_detected = FALSE;    // if game should be continued
11758
11759     free(message);
11760
11761     return;
11762   }
11763
11764   if (game.restart_level)
11765     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11766
11767   CheckLevelSolved();
11768
11769   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11770     GameWon();
11771
11772   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11773     TapeStop();
11774
11775   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11776     return;
11777
11778   game_frame_delay_value =
11779     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11780
11781   if (tape.playing && tape.warp_forward && !tape.pausing)
11782     game_frame_delay_value = 0;
11783
11784   SetVideoFrameDelay(game_frame_delay_value);
11785
11786   // (de)activate virtual buttons depending on current game status
11787   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11788   {
11789     if (game.all_players_gone)  // if no players there to be controlled anymore
11790       SetOverlayActive(FALSE);
11791     else if (!tape.playing)     // if game continues after tape stopped playing
11792       SetOverlayActive(TRUE);
11793   }
11794
11795 #if 0
11796 #if 0
11797   // ---------- main game synchronization point ----------
11798
11799   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11800
11801   Debug("game:playing:skip", "skip == %d", skip);
11802
11803 #else
11804   // ---------- main game synchronization point ----------
11805
11806   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11807 #endif
11808 #endif
11809
11810   if (network_playing && !network_player_action_received)
11811   {
11812     // try to get network player actions in time
11813
11814     // last chance to get network player actions without main loop delay
11815     HandleNetworking();
11816
11817     // game was quit by network peer
11818     if (game_status != GAME_MODE_PLAYING)
11819       return;
11820
11821     // check if network player actions still missing and game still running
11822     if (!network_player_action_received && !checkGameEnded())
11823       return;           // failed to get network player actions in time
11824
11825     // do not yet reset "network_player_action_received" (for tape.pausing)
11826   }
11827
11828   if (tape.pausing)
11829     return;
11830
11831   // at this point we know that we really continue executing the game
11832
11833   network_player_action_received = FALSE;
11834
11835   // when playing tape, read previously recorded player input from tape data
11836   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11837
11838   local_player->effective_mouse_action = local_player->mouse_action;
11839
11840   if (recorded_player_action != NULL)
11841     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11842                                  recorded_player_action);
11843
11844   // TapePlayAction() may return NULL when toggling to "pause before death"
11845   if (tape.pausing)
11846     return;
11847
11848   if (tape.set_centered_player)
11849   {
11850     game.centered_player_nr_next = tape.centered_player_nr_next;
11851     game.set_centered_player = TRUE;
11852   }
11853
11854   for (i = 0; i < MAX_PLAYERS; i++)
11855   {
11856     summarized_player_action |= stored_player[i].action;
11857
11858     if (!network_playing && (game.team_mode || tape.playing))
11859       stored_player[i].effective_action = stored_player[i].action;
11860   }
11861
11862   if (network_playing && !checkGameEnded())
11863     SendToServer_MovePlayer(summarized_player_action);
11864
11865   // summarize all actions at local players mapped input device position
11866   // (this allows using different input devices in single player mode)
11867   if (!network.enabled && !game.team_mode)
11868     stored_player[map_player_action[local_player->index_nr]].effective_action =
11869       summarized_player_action;
11870
11871   // summarize all actions at centered player in local team mode
11872   if (tape.recording &&
11873       setup.team_mode && !network.enabled &&
11874       setup.input_on_focus &&
11875       game.centered_player_nr != -1)
11876   {
11877     for (i = 0; i < MAX_PLAYERS; i++)
11878       stored_player[map_player_action[i]].effective_action =
11879         (i == game.centered_player_nr ? summarized_player_action : 0);
11880   }
11881
11882   if (recorded_player_action != NULL)
11883     for (i = 0; i < MAX_PLAYERS; i++)
11884       stored_player[i].effective_action = recorded_player_action[i];
11885
11886   for (i = 0; i < MAX_PLAYERS; i++)
11887   {
11888     tape_action[i] = stored_player[i].effective_action;
11889
11890     /* (this may happen in the RND game engine if a player was not present on
11891        the playfield on level start, but appeared later from a custom element */
11892     if (setup.team_mode &&
11893         tape.recording &&
11894         tape_action[i] &&
11895         !tape.player_participates[i])
11896       tape.player_participates[i] = TRUE;
11897   }
11898
11899   SetTapeActionFromMouseAction(tape_action,
11900                                &local_player->effective_mouse_action);
11901
11902   // only record actions from input devices, but not programmed actions
11903   if (tape.recording)
11904     TapeRecordAction(tape_action);
11905
11906   // remember if game was played (especially after tape stopped playing)
11907   if (!tape.playing && summarized_player_action && !checkGameFailed())
11908     game.GamePlayed = TRUE;
11909
11910 #if USE_NEW_PLAYER_ASSIGNMENTS
11911   // !!! also map player actions in single player mode !!!
11912   // if (game.team_mode)
11913   if (1)
11914   {
11915     byte mapped_action[MAX_PLAYERS];
11916
11917 #if DEBUG_PLAYER_ACTIONS
11918     for (i = 0; i < MAX_PLAYERS; i++)
11919       DebugContinued("", "%d, ", stored_player[i].effective_action);
11920 #endif
11921
11922     for (i = 0; i < MAX_PLAYERS; i++)
11923       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11924
11925     for (i = 0; i < MAX_PLAYERS; i++)
11926       stored_player[i].effective_action = mapped_action[i];
11927
11928 #if DEBUG_PLAYER_ACTIONS
11929     DebugContinued("", "=> ");
11930     for (i = 0; i < MAX_PLAYERS; i++)
11931       DebugContinued("", "%d, ", stored_player[i].effective_action);
11932     DebugContinued("game:playing:player", "\n");
11933 #endif
11934   }
11935 #if DEBUG_PLAYER_ACTIONS
11936   else
11937   {
11938     for (i = 0; i < MAX_PLAYERS; i++)
11939       DebugContinued("", "%d, ", stored_player[i].effective_action);
11940     DebugContinued("game:playing:player", "\n");
11941   }
11942 #endif
11943 #endif
11944
11945   for (i = 0; i < MAX_PLAYERS; i++)
11946   {
11947     // allow engine snapshot in case of changed movement attempt
11948     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11949         (stored_player[i].effective_action & KEY_MOTION))
11950       game.snapshot.changed_action = TRUE;
11951
11952     // allow engine snapshot in case of snapping/dropping attempt
11953     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11954         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11955       game.snapshot.changed_action = TRUE;
11956
11957     game.snapshot.last_action[i] = stored_player[i].effective_action;
11958   }
11959
11960   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11961   {
11962     GameActions_EM_Main();
11963   }
11964   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11965   {
11966     GameActions_SP_Main();
11967   }
11968   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11969   {
11970     GameActions_MM_Main();
11971   }
11972   else
11973   {
11974     GameActions_RND_Main();
11975   }
11976
11977   BlitScreenToBitmap(backbuffer);
11978
11979   CheckLevelSolved();
11980   CheckLevelTime();
11981
11982   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11983
11984   if (global.show_frames_per_second)
11985   {
11986     static unsigned int fps_counter = 0;
11987     static int fps_frames = 0;
11988     unsigned int fps_delay_ms = Counter() - fps_counter;
11989
11990     fps_frames++;
11991
11992     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11993     {
11994       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11995
11996       fps_frames = 0;
11997       fps_counter = Counter();
11998
11999       // always draw FPS to screen after FPS value was updated
12000       redraw_mask |= REDRAW_FPS;
12001     }
12002
12003     // only draw FPS if no screen areas are deactivated (invisible warp mode)
12004     if (GetDrawDeactivationMask() == REDRAW_NONE)
12005       redraw_mask |= REDRAW_FPS;
12006   }
12007 }
12008
12009 static void GameActions_CheckSaveEngineSnapshot(void)
12010 {
12011   if (!game.snapshot.save_snapshot)
12012     return;
12013
12014   // clear flag for saving snapshot _before_ saving snapshot
12015   game.snapshot.save_snapshot = FALSE;
12016
12017   SaveEngineSnapshotToList();
12018 }
12019
12020 void GameActions(void)
12021 {
12022   GameActionsExt();
12023
12024   GameActions_CheckSaveEngineSnapshot();
12025 }
12026
12027 void GameActions_EM_Main(void)
12028 {
12029   byte effective_action[MAX_PLAYERS];
12030   int i;
12031
12032   for (i = 0; i < MAX_PLAYERS; i++)
12033     effective_action[i] = stored_player[i].effective_action;
12034
12035   GameActions_EM(effective_action);
12036 }
12037
12038 void GameActions_SP_Main(void)
12039 {
12040   byte effective_action[MAX_PLAYERS];
12041   int i;
12042
12043   for (i = 0; i < MAX_PLAYERS; i++)
12044     effective_action[i] = stored_player[i].effective_action;
12045
12046   GameActions_SP(effective_action);
12047
12048   for (i = 0; i < MAX_PLAYERS; i++)
12049   {
12050     if (stored_player[i].force_dropping)
12051       stored_player[i].action |= KEY_BUTTON_DROP;
12052
12053     stored_player[i].force_dropping = FALSE;
12054   }
12055 }
12056
12057 void GameActions_MM_Main(void)
12058 {
12059   AdvanceGfxFrame();
12060
12061   GameActions_MM(local_player->effective_mouse_action);
12062 }
12063
12064 void GameActions_RND_Main(void)
12065 {
12066   GameActions_RND();
12067 }
12068
12069 void GameActions_RND(void)
12070 {
12071   static struct MouseActionInfo mouse_action_last = { 0 };
12072   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12073   int magic_wall_x = 0, magic_wall_y = 0;
12074   int i, x, y, element, graphic, last_gfx_frame;
12075
12076   InitPlayfieldScanModeVars();
12077
12078   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12079   {
12080     SCAN_PLAYFIELD(x, y)
12081     {
12082       ChangeCount[x][y] = 0;
12083       ChangeEvent[x][y] = -1;
12084     }
12085   }
12086
12087   if (game.set_centered_player)
12088   {
12089     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12090
12091     // switching to "all players" only possible if all players fit to screen
12092     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12093     {
12094       game.centered_player_nr_next = game.centered_player_nr;
12095       game.set_centered_player = FALSE;
12096     }
12097
12098     // do not switch focus to non-existing (or non-active) player
12099     if (game.centered_player_nr_next >= 0 &&
12100         !stored_player[game.centered_player_nr_next].active)
12101     {
12102       game.centered_player_nr_next = game.centered_player_nr;
12103       game.set_centered_player = FALSE;
12104     }
12105   }
12106
12107   if (game.set_centered_player &&
12108       ScreenMovPos == 0)        // screen currently aligned at tile position
12109   {
12110     int sx, sy;
12111
12112     if (game.centered_player_nr_next == -1)
12113     {
12114       setScreenCenteredToAllPlayers(&sx, &sy);
12115     }
12116     else
12117     {
12118       sx = stored_player[game.centered_player_nr_next].jx;
12119       sy = stored_player[game.centered_player_nr_next].jy;
12120     }
12121
12122     game.centered_player_nr = game.centered_player_nr_next;
12123     game.set_centered_player = FALSE;
12124
12125     DrawRelocateScreen(0, 0, sx, sy, TRUE, setup.quick_switch);
12126     DrawGameDoorValues();
12127   }
12128
12129   // check single step mode (set flag and clear again if any player is active)
12130   game.enter_single_step_mode =
12131     (tape.single_step && tape.recording && !tape.pausing);
12132
12133   for (i = 0; i < MAX_PLAYERS; i++)
12134   {
12135     int actual_player_action = stored_player[i].effective_action;
12136
12137 #if 1
12138     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12139        - rnd_equinox_tetrachloride 048
12140        - rnd_equinox_tetrachloride_ii 096
12141        - rnd_emanuel_schmieg 002
12142        - doctor_sloan_ww 001, 020
12143     */
12144     if (stored_player[i].MovPos == 0)
12145       CheckGravityMovement(&stored_player[i]);
12146 #endif
12147
12148     // overwrite programmed action with tape action
12149     if (stored_player[i].programmed_action)
12150       actual_player_action = stored_player[i].programmed_action;
12151
12152     PlayerActions(&stored_player[i], actual_player_action);
12153
12154     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12155   }
12156
12157   // single step pause mode may already have been toggled by "ScrollPlayer()"
12158   if (game.enter_single_step_mode && !tape.pausing)
12159     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12160
12161   ScrollScreen(NULL, SCROLL_GO_ON);
12162
12163   /* for backwards compatibility, the following code emulates a fixed bug that
12164      occured when pushing elements (causing elements that just made their last
12165      pushing step to already (if possible) make their first falling step in the
12166      same game frame, which is bad); this code is also needed to use the famous
12167      "spring push bug" which is used in older levels and might be wanted to be
12168      used also in newer levels, but in this case the buggy pushing code is only
12169      affecting the "spring" element and no other elements */
12170
12171   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12172   {
12173     for (i = 0; i < MAX_PLAYERS; i++)
12174     {
12175       struct PlayerInfo *player = &stored_player[i];
12176       int x = player->jx;
12177       int y = player->jy;
12178
12179       if (player->active && player->is_pushing && player->is_moving &&
12180           IS_MOVING(x, y) &&
12181           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12182            Tile[x][y] == EL_SPRING))
12183       {
12184         ContinueMoving(x, y);
12185
12186         // continue moving after pushing (this is actually a bug)
12187         if (!IS_MOVING(x, y))
12188           Stop[x][y] = FALSE;
12189       }
12190     }
12191   }
12192
12193   SCAN_PLAYFIELD(x, y)
12194   {
12195     Last[x][y] = Tile[x][y];
12196
12197     ChangeCount[x][y] = 0;
12198     ChangeEvent[x][y] = -1;
12199
12200     // this must be handled before main playfield loop
12201     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12202     {
12203       MovDelay[x][y]--;
12204       if (MovDelay[x][y] <= 0)
12205         RemoveField(x, y);
12206     }
12207
12208     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12209     {
12210       MovDelay[x][y]--;
12211       if (MovDelay[x][y] <= 0)
12212       {
12213         int element = Store[x][y];
12214         int move_direction = MovDir[x][y];
12215         int player_index_bit = Store2[x][y];
12216
12217         Store[x][y] = 0;
12218         Store2[x][y] = 0;
12219
12220         RemoveField(x, y);
12221         TEST_DrawLevelField(x, y);
12222
12223         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12224
12225         if (IS_ENVELOPE(element))
12226           local_player->show_envelope = element;
12227       }
12228     }
12229
12230 #if DEBUG
12231     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12232     {
12233       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12234             x, y);
12235       Debug("game:playing:GameActions_RND", "This should never happen!");
12236
12237       ChangePage[x][y] = -1;
12238     }
12239 #endif
12240
12241     Stop[x][y] = FALSE;
12242     if (WasJustMoving[x][y] > 0)
12243       WasJustMoving[x][y]--;
12244     if (WasJustFalling[x][y] > 0)
12245       WasJustFalling[x][y]--;
12246     if (CheckCollision[x][y] > 0)
12247       CheckCollision[x][y]--;
12248     if (CheckImpact[x][y] > 0)
12249       CheckImpact[x][y]--;
12250
12251     GfxFrame[x][y]++;
12252
12253     /* reset finished pushing action (not done in ContinueMoving() to allow
12254        continuous pushing animation for elements with zero push delay) */
12255     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12256     {
12257       ResetGfxAnimation(x, y);
12258       TEST_DrawLevelField(x, y);
12259     }
12260
12261 #if DEBUG
12262     if (IS_BLOCKED(x, y))
12263     {
12264       int oldx, oldy;
12265
12266       Blocked2Moving(x, y, &oldx, &oldy);
12267       if (!IS_MOVING(oldx, oldy))
12268       {
12269         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12270         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12271         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12272         Debug("game:playing:GameActions_RND", "This should never happen!");
12273       }
12274     }
12275 #endif
12276   }
12277
12278   if (mouse_action.button)
12279   {
12280     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12281     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action.button);
12282
12283     x = mouse_action.lx;
12284     y = mouse_action.ly;
12285     element = Tile[x][y];
12286
12287     if (new_button)
12288     {
12289       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
12290       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
12291                                          ch_button);
12292     }
12293
12294     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
12295     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
12296                                        ch_button);
12297
12298     if (level.use_step_counter)
12299     {
12300       boolean counted_click = FALSE;
12301
12302       // element clicked that can change when clicked/pressed
12303       if (CAN_CHANGE_OR_HAS_ACTION(element) &&
12304           (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
12305            HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE)))
12306         counted_click = TRUE;
12307
12308       // element clicked that can trigger change when clicked/pressed
12309       if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] ||
12310           trigger_events[element][CE_MOUSE_PRESSED_ON_X])
12311         counted_click = TRUE;
12312
12313       if (new_button && counted_click)
12314         CheckLevelTime_StepCounter();
12315     }
12316   }
12317
12318   SCAN_PLAYFIELD(x, y)
12319   {
12320     element = Tile[x][y];
12321     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12322     last_gfx_frame = GfxFrame[x][y];
12323
12324     if (element == EL_EMPTY)
12325       graphic = el2img(GfxElementEmpty[x][y]);
12326
12327     ResetGfxFrame(x, y);
12328
12329     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12330       DrawLevelGraphicAnimation(x, y, graphic);
12331
12332     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12333         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12334       ResetRandomAnimationValue(x, y);
12335
12336     SetRandomAnimationValue(x, y);
12337
12338     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12339
12340     if (IS_INACTIVE(element))
12341     {
12342       if (IS_ANIMATED(graphic))
12343         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12344
12345       continue;
12346     }
12347
12348     // this may take place after moving, so 'element' may have changed
12349     if (IS_CHANGING(x, y) &&
12350         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12351     {
12352       int page = element_info[element].event_page_nr[CE_DELAY];
12353
12354       HandleElementChange(x, y, page);
12355
12356       element = Tile[x][y];
12357       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12358     }
12359
12360     CheckNextToConditions(x, y);
12361
12362     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12363     {
12364       StartMoving(x, y);
12365
12366       element = Tile[x][y];
12367       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12368
12369       if (IS_ANIMATED(graphic) &&
12370           !IS_MOVING(x, y) &&
12371           !Stop[x][y])
12372         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12373
12374       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12375         TEST_DrawTwinkleOnField(x, y);
12376     }
12377     else if (element == EL_ACID)
12378     {
12379       if (!Stop[x][y])
12380         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12381     }
12382     else if ((element == EL_EXIT_OPEN ||
12383               element == EL_EM_EXIT_OPEN ||
12384               element == EL_SP_EXIT_OPEN ||
12385               element == EL_STEEL_EXIT_OPEN ||
12386               element == EL_EM_STEEL_EXIT_OPEN ||
12387               element == EL_SP_TERMINAL ||
12388               element == EL_SP_TERMINAL_ACTIVE ||
12389               element == EL_EXTRA_TIME ||
12390               element == EL_SHIELD_NORMAL ||
12391               element == EL_SHIELD_DEADLY) &&
12392              IS_ANIMATED(graphic))
12393       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12394     else if (IS_MOVING(x, y))
12395       ContinueMoving(x, y);
12396     else if (IS_ACTIVE_BOMB(element))
12397       CheckDynamite(x, y);
12398     else if (element == EL_AMOEBA_GROWING)
12399       AmoebaGrowing(x, y);
12400     else if (element == EL_AMOEBA_SHRINKING)
12401       AmoebaShrinking(x, y);
12402
12403 #if !USE_NEW_AMOEBA_CODE
12404     else if (IS_AMOEBALIVE(element))
12405       AmoebaReproduce(x, y);
12406 #endif
12407
12408     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12409       Life(x, y);
12410     else if (element == EL_EXIT_CLOSED)
12411       CheckExit(x, y);
12412     else if (element == EL_EM_EXIT_CLOSED)
12413       CheckExitEM(x, y);
12414     else if (element == EL_STEEL_EXIT_CLOSED)
12415       CheckExitSteel(x, y);
12416     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12417       CheckExitSteelEM(x, y);
12418     else if (element == EL_SP_EXIT_CLOSED)
12419       CheckExitSP(x, y);
12420     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12421              element == EL_EXPANDABLE_STEELWALL_GROWING)
12422       WallGrowing(x, y);
12423     else if (element == EL_EXPANDABLE_WALL ||
12424              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12425              element == EL_EXPANDABLE_WALL_VERTICAL ||
12426              element == EL_EXPANDABLE_WALL_ANY ||
12427              element == EL_BD_EXPANDABLE_WALL ||
12428              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12429              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12430              element == EL_EXPANDABLE_STEELWALL_ANY)
12431       CheckWallGrowing(x, y);
12432     else if (element == EL_FLAMES)
12433       CheckForDragon(x, y);
12434     else if (element == EL_EXPLOSION)
12435       ; // drawing of correct explosion animation is handled separately
12436     else if (element == EL_ELEMENT_SNAPPING ||
12437              element == EL_DIAGONAL_SHRINKING ||
12438              element == EL_DIAGONAL_GROWING)
12439     {
12440       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y], GfxDir[x][y]);
12441
12442       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12443     }
12444     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12445       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12446
12447     if (IS_BELT_ACTIVE(element))
12448       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12449
12450     if (game.magic_wall_active)
12451     {
12452       int jx = local_player->jx, jy = local_player->jy;
12453
12454       // play the element sound at the position nearest to the player
12455       if ((element == EL_MAGIC_WALL_FULL ||
12456            element == EL_MAGIC_WALL_ACTIVE ||
12457            element == EL_MAGIC_WALL_EMPTYING ||
12458            element == EL_BD_MAGIC_WALL_FULL ||
12459            element == EL_BD_MAGIC_WALL_ACTIVE ||
12460            element == EL_BD_MAGIC_WALL_EMPTYING ||
12461            element == EL_DC_MAGIC_WALL_FULL ||
12462            element == EL_DC_MAGIC_WALL_ACTIVE ||
12463            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12464           ABS(x - jx) + ABS(y - jy) <
12465           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12466       {
12467         magic_wall_x = x;
12468         magic_wall_y = y;
12469       }
12470     }
12471   }
12472
12473 #if USE_NEW_AMOEBA_CODE
12474   // new experimental amoeba growth stuff
12475   if (!(FrameCounter % 8))
12476   {
12477     static unsigned int random = 1684108901;
12478
12479     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12480     {
12481       x = RND(lev_fieldx);
12482       y = RND(lev_fieldy);
12483       element = Tile[x][y];
12484
12485       if (!IS_PLAYER(x, y) &&
12486           (element == EL_EMPTY ||
12487            CAN_GROW_INTO(element) ||
12488            element == EL_QUICKSAND_EMPTY ||
12489            element == EL_QUICKSAND_FAST_EMPTY ||
12490            element == EL_ACID_SPLASH_LEFT ||
12491            element == EL_ACID_SPLASH_RIGHT))
12492       {
12493         if ((IN_LEV_FIELD(x, y - 1) && Tile[x][y - 1] == EL_AMOEBA_WET) ||
12494             (IN_LEV_FIELD(x - 1, y) && Tile[x - 1][y] == EL_AMOEBA_WET) ||
12495             (IN_LEV_FIELD(x + 1, y) && Tile[x + 1][y] == EL_AMOEBA_WET) ||
12496             (IN_LEV_FIELD(x, y + 1) && Tile[x][y + 1] == EL_AMOEBA_WET))
12497           Tile[x][y] = EL_AMOEBA_DROP;
12498       }
12499
12500       random = random * 129 + 1;
12501     }
12502   }
12503 #endif
12504
12505   game.explosions_delayed = FALSE;
12506
12507   SCAN_PLAYFIELD(x, y)
12508   {
12509     element = Tile[x][y];
12510
12511     if (ExplodeField[x][y])
12512       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12513     else if (element == EL_EXPLOSION)
12514       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12515
12516     ExplodeField[x][y] = EX_TYPE_NONE;
12517   }
12518
12519   game.explosions_delayed = TRUE;
12520
12521   if (game.magic_wall_active)
12522   {
12523     if (!(game.magic_wall_time_left % 4))
12524     {
12525       int element = Tile[magic_wall_x][magic_wall_y];
12526
12527       if (element == EL_BD_MAGIC_WALL_FULL ||
12528           element == EL_BD_MAGIC_WALL_ACTIVE ||
12529           element == EL_BD_MAGIC_WALL_EMPTYING)
12530         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12531       else if (element == EL_DC_MAGIC_WALL_FULL ||
12532                element == EL_DC_MAGIC_WALL_ACTIVE ||
12533                element == EL_DC_MAGIC_WALL_EMPTYING)
12534         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12535       else
12536         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12537     }
12538
12539     if (game.magic_wall_time_left > 0)
12540     {
12541       game.magic_wall_time_left--;
12542
12543       if (!game.magic_wall_time_left)
12544       {
12545         SCAN_PLAYFIELD(x, y)
12546         {
12547           element = Tile[x][y];
12548
12549           if (element == EL_MAGIC_WALL_ACTIVE ||
12550               element == EL_MAGIC_WALL_FULL)
12551           {
12552             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12553             TEST_DrawLevelField(x, y);
12554           }
12555           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12556                    element == EL_BD_MAGIC_WALL_FULL)
12557           {
12558             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12559             TEST_DrawLevelField(x, y);
12560           }
12561           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12562                    element == EL_DC_MAGIC_WALL_FULL)
12563           {
12564             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12565             TEST_DrawLevelField(x, y);
12566           }
12567         }
12568
12569         game.magic_wall_active = FALSE;
12570       }
12571     }
12572   }
12573
12574   if (game.light_time_left > 0)
12575   {
12576     game.light_time_left--;
12577
12578     if (game.light_time_left == 0)
12579       RedrawAllLightSwitchesAndInvisibleElements();
12580   }
12581
12582   if (game.timegate_time_left > 0)
12583   {
12584     game.timegate_time_left--;
12585
12586     if (game.timegate_time_left == 0)
12587       CloseAllOpenTimegates();
12588   }
12589
12590   if (game.lenses_time_left > 0)
12591   {
12592     game.lenses_time_left--;
12593
12594     if (game.lenses_time_left == 0)
12595       RedrawAllInvisibleElementsForLenses();
12596   }
12597
12598   if (game.magnify_time_left > 0)
12599   {
12600     game.magnify_time_left--;
12601
12602     if (game.magnify_time_left == 0)
12603       RedrawAllInvisibleElementsForMagnifier();
12604   }
12605
12606   for (i = 0; i < MAX_PLAYERS; i++)
12607   {
12608     struct PlayerInfo *player = &stored_player[i];
12609
12610     if (SHIELD_ON(player))
12611     {
12612       if (player->shield_deadly_time_left)
12613         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12614       else if (player->shield_normal_time_left)
12615         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12616     }
12617   }
12618
12619 #if USE_DELAYED_GFX_REDRAW
12620   SCAN_PLAYFIELD(x, y)
12621   {
12622     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12623     {
12624       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12625          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12626
12627       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12628         DrawLevelField(x, y);
12629
12630       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12631         DrawLevelFieldCrumbled(x, y);
12632
12633       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12634         DrawLevelFieldCrumbledNeighbours(x, y);
12635
12636       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12637         DrawTwinkleOnField(x, y);
12638     }
12639
12640     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12641   }
12642 #endif
12643
12644   DrawAllPlayers();
12645   PlayAllPlayersSound();
12646
12647   for (i = 0; i < MAX_PLAYERS; i++)
12648   {
12649     struct PlayerInfo *player = &stored_player[i];
12650
12651     if (player->show_envelope != 0 && (!player->active ||
12652                                        player->MovPos == 0))
12653     {
12654       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12655
12656       player->show_envelope = 0;
12657     }
12658   }
12659
12660   // use random number generator in every frame to make it less predictable
12661   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12662     RND(1);
12663
12664   mouse_action_last = mouse_action;
12665 }
12666
12667 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12668 {
12669   int min_x = x, min_y = y, max_x = x, max_y = y;
12670   int scr_fieldx = getScreenFieldSizeX();
12671   int scr_fieldy = getScreenFieldSizeY();
12672   int i;
12673
12674   for (i = 0; i < MAX_PLAYERS; i++)
12675   {
12676     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12677
12678     if (!stored_player[i].active || &stored_player[i] == player)
12679       continue;
12680
12681     min_x = MIN(min_x, jx);
12682     min_y = MIN(min_y, jy);
12683     max_x = MAX(max_x, jx);
12684     max_y = MAX(max_y, jy);
12685   }
12686
12687   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12688 }
12689
12690 static boolean AllPlayersInVisibleScreen(void)
12691 {
12692   int i;
12693
12694   for (i = 0; i < MAX_PLAYERS; i++)
12695   {
12696     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12697
12698     if (!stored_player[i].active)
12699       continue;
12700
12701     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12702       return FALSE;
12703   }
12704
12705   return TRUE;
12706 }
12707
12708 void ScrollLevel(int dx, int dy)
12709 {
12710   int scroll_offset = 2 * TILEX_VAR;
12711   int x, y;
12712
12713   BlitBitmap(drawto_field, drawto_field,
12714              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12715              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12716              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12717              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12718              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12719              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12720
12721   if (dx != 0)
12722   {
12723     x = (dx == 1 ? BX1 : BX2);
12724     for (y = BY1; y <= BY2; y++)
12725       DrawScreenField(x, y);
12726   }
12727
12728   if (dy != 0)
12729   {
12730     y = (dy == 1 ? BY1 : BY2);
12731     for (x = BX1; x <= BX2; x++)
12732       DrawScreenField(x, y);
12733   }
12734
12735   redraw_mask |= REDRAW_FIELD;
12736 }
12737
12738 static boolean canFallDown(struct PlayerInfo *player)
12739 {
12740   int jx = player->jx, jy = player->jy;
12741
12742   return (IN_LEV_FIELD(jx, jy + 1) &&
12743           (IS_FREE(jx, jy + 1) ||
12744            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12745           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12746           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12747 }
12748
12749 static boolean canPassField(int x, int y, int move_dir)
12750 {
12751   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12752   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12753   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12754   int nextx = x + dx;
12755   int nexty = y + dy;
12756   int element = Tile[x][y];
12757
12758   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12759           !CAN_MOVE(element) &&
12760           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12761           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12762           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12763 }
12764
12765 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12766 {
12767   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12768   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12769   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12770   int newx = x + dx;
12771   int newy = y + dy;
12772
12773   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12774           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12775           (IS_DIGGABLE(Tile[newx][newy]) ||
12776            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12777            canPassField(newx, newy, move_dir)));
12778 }
12779
12780 static void CheckGravityMovement(struct PlayerInfo *player)
12781 {
12782   if (player->gravity && !player->programmed_action)
12783   {
12784     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12785     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12786     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12787     int jx = player->jx, jy = player->jy;
12788     boolean player_is_moving_to_valid_field =
12789       (!player_is_snapping &&
12790        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12791         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12792     boolean player_can_fall_down = canFallDown(player);
12793
12794     if (player_can_fall_down &&
12795         !player_is_moving_to_valid_field)
12796       player->programmed_action = MV_DOWN;
12797   }
12798 }
12799
12800 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12801 {
12802   return CheckGravityMovement(player);
12803
12804   if (player->gravity && !player->programmed_action)
12805   {
12806     int jx = player->jx, jy = player->jy;
12807     boolean field_under_player_is_free =
12808       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12809     boolean player_is_standing_on_valid_field =
12810       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12811        (IS_WALKABLE(Tile[jx][jy]) &&
12812         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12813
12814     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12815       player->programmed_action = MV_DOWN;
12816   }
12817 }
12818
12819 /*
12820   MovePlayerOneStep()
12821   -----------------------------------------------------------------------------
12822   dx, dy:               direction (non-diagonal) to try to move the player to
12823   real_dx, real_dy:     direction as read from input device (can be diagonal)
12824 */
12825
12826 boolean MovePlayerOneStep(struct PlayerInfo *player,
12827                           int dx, int dy, int real_dx, int real_dy)
12828 {
12829   int jx = player->jx, jy = player->jy;
12830   int new_jx = jx + dx, new_jy = jy + dy;
12831   int can_move;
12832   boolean player_can_move = !player->cannot_move;
12833
12834   if (!player->active || (!dx && !dy))
12835     return MP_NO_ACTION;
12836
12837   player->MovDir = (dx < 0 ? MV_LEFT :
12838                     dx > 0 ? MV_RIGHT :
12839                     dy < 0 ? MV_UP :
12840                     dy > 0 ? MV_DOWN :  MV_NONE);
12841
12842   if (!IN_LEV_FIELD(new_jx, new_jy))
12843     return MP_NO_ACTION;
12844
12845   if (!player_can_move)
12846   {
12847     if (player->MovPos == 0)
12848     {
12849       player->is_moving = FALSE;
12850       player->is_digging = FALSE;
12851       player->is_collecting = FALSE;
12852       player->is_snapping = FALSE;
12853       player->is_pushing = FALSE;
12854     }
12855   }
12856
12857   if (!network.enabled && game.centered_player_nr == -1 &&
12858       !AllPlayersInSight(player, new_jx, new_jy))
12859     return MP_NO_ACTION;
12860
12861   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx, real_dy, DF_DIG);
12862   if (can_move != MP_MOVING)
12863     return can_move;
12864
12865   // check if DigField() has caused relocation of the player
12866   if (player->jx != jx || player->jy != jy)
12867     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12868
12869   StorePlayer[jx][jy] = 0;
12870   player->last_jx = jx;
12871   player->last_jy = jy;
12872   player->jx = new_jx;
12873   player->jy = new_jy;
12874   StorePlayer[new_jx][new_jy] = player->element_nr;
12875
12876   if (player->move_delay_value_next != -1)
12877   {
12878     player->move_delay_value = player->move_delay_value_next;
12879     player->move_delay_value_next = -1;
12880   }
12881
12882   player->MovPos =
12883     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12884
12885   player->step_counter++;
12886
12887   PlayerVisit[jx][jy] = FrameCounter;
12888
12889   player->is_moving = TRUE;
12890
12891 #if 1
12892   // should better be called in MovePlayer(), but this breaks some tapes
12893   ScrollPlayer(player, SCROLL_INIT);
12894 #endif
12895
12896   return MP_MOVING;
12897 }
12898
12899 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12900 {
12901   int jx = player->jx, jy = player->jy;
12902   int old_jx = jx, old_jy = jy;
12903   int moved = MP_NO_ACTION;
12904
12905   if (!player->active)
12906     return FALSE;
12907
12908   if (!dx && !dy)
12909   {
12910     if (player->MovPos == 0)
12911     {
12912       player->is_moving = FALSE;
12913       player->is_digging = FALSE;
12914       player->is_collecting = FALSE;
12915       player->is_snapping = FALSE;
12916       player->is_pushing = FALSE;
12917     }
12918
12919     return FALSE;
12920   }
12921
12922   if (player->move_delay > 0)
12923     return FALSE;
12924
12925   player->move_delay = -1;              // set to "uninitialized" value
12926
12927   // store if player is automatically moved to next field
12928   player->is_auto_moving = (player->programmed_action != MV_NONE);
12929
12930   // remove the last programmed player action
12931   player->programmed_action = 0;
12932
12933   if (player->MovPos)
12934   {
12935     // should only happen if pre-1.2 tape recordings are played
12936     // this is only for backward compatibility
12937
12938     int original_move_delay_value = player->move_delay_value;
12939
12940 #if DEBUG
12941     Debug("game:playing:MovePlayer",
12942           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12943           tape.counter);
12944 #endif
12945
12946     // scroll remaining steps with finest movement resolution
12947     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12948
12949     while (player->MovPos)
12950     {
12951       ScrollPlayer(player, SCROLL_GO_ON);
12952       ScrollScreen(NULL, SCROLL_GO_ON);
12953
12954       AdvanceFrameAndPlayerCounters(player->index_nr);
12955
12956       DrawAllPlayers();
12957       BackToFront_WithFrameDelay(0);
12958     }
12959
12960     player->move_delay_value = original_move_delay_value;
12961   }
12962
12963   player->is_active = FALSE;
12964
12965   if (player->last_move_dir & MV_HORIZONTAL)
12966   {
12967     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12968       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12969   }
12970   else
12971   {
12972     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12973       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12974   }
12975
12976   if (!moved && !player->is_active)
12977   {
12978     player->is_moving = FALSE;
12979     player->is_digging = FALSE;
12980     player->is_collecting = FALSE;
12981     player->is_snapping = FALSE;
12982     player->is_pushing = FALSE;
12983   }
12984
12985   jx = player->jx;
12986   jy = player->jy;
12987
12988   if (moved & MP_MOVING && !ScreenMovPos &&
12989       (player->index_nr == game.centered_player_nr ||
12990        game.centered_player_nr == -1))
12991   {
12992     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12993
12994     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12995     {
12996       // actual player has left the screen -- scroll in that direction
12997       if (jx != old_jx)         // player has moved horizontally
12998         scroll_x += (jx - old_jx);
12999       else                      // player has moved vertically
13000         scroll_y += (jy - old_jy);
13001     }
13002     else
13003     {
13004       int offset_raw = game.scroll_delay_value;
13005
13006       if (jx != old_jx)         // player has moved horizontally
13007       {
13008         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
13009         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
13010         int new_scroll_x = jx - MIDPOSX + offset_x;
13011
13012         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
13013             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
13014           scroll_x = new_scroll_x;
13015
13016         // don't scroll over playfield boundaries
13017         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
13018
13019         // don't scroll more than one field at a time
13020         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13021
13022         // don't scroll against the player's moving direction
13023         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13024             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13025           scroll_x = old_scroll_x;
13026       }
13027       else                      // player has moved vertically
13028       {
13029         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
13030         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
13031         int new_scroll_y = jy - MIDPOSY + offset_y;
13032
13033         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
13034             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13035           scroll_y = new_scroll_y;
13036
13037         // don't scroll over playfield boundaries
13038         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13039
13040         // don't scroll more than one field at a time
13041         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13042
13043         // don't scroll against the player's moving direction
13044         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13045             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13046           scroll_y = old_scroll_y;
13047       }
13048     }
13049
13050     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13051     {
13052       if (!network.enabled && game.centered_player_nr == -1 &&
13053           !AllPlayersInVisibleScreen())
13054       {
13055         scroll_x = old_scroll_x;
13056         scroll_y = old_scroll_y;
13057       }
13058       else
13059       {
13060         ScrollScreen(player, SCROLL_INIT);
13061         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13062       }
13063     }
13064   }
13065
13066   player->StepFrame = 0;
13067
13068   if (moved & MP_MOVING)
13069   {
13070     if (old_jx != jx && old_jy == jy)
13071       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13072     else if (old_jx == jx && old_jy != jy)
13073       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13074
13075     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13076
13077     player->last_move_dir = player->MovDir;
13078     player->is_moving = TRUE;
13079     player->is_snapping = FALSE;
13080     player->is_switching = FALSE;
13081     player->is_dropping = FALSE;
13082     player->is_dropping_pressed = FALSE;
13083     player->drop_pressed_delay = 0;
13084
13085 #if 0
13086     // should better be called here than above, but this breaks some tapes
13087     ScrollPlayer(player, SCROLL_INIT);
13088 #endif
13089   }
13090   else
13091   {
13092     CheckGravityMovementWhenNotMoving(player);
13093
13094     player->is_moving = FALSE;
13095
13096     /* at this point, the player is allowed to move, but cannot move right now
13097        (e.g. because of something blocking the way) -- ensure that the player
13098        is also allowed to move in the next frame (in old versions before 3.1.1,
13099        the player was forced to wait again for eight frames before next try) */
13100
13101     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13102       player->move_delay = 0;   // allow direct movement in the next frame
13103   }
13104
13105   if (player->move_delay == -1)         // not yet initialized by DigField()
13106     player->move_delay = player->move_delay_value;
13107
13108   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13109   {
13110     TestIfPlayerTouchesBadThing(jx, jy);
13111     TestIfPlayerTouchesCustomElement(jx, jy);
13112   }
13113
13114   if (!player->active)
13115     RemovePlayer(player);
13116
13117   return moved;
13118 }
13119
13120 void ScrollPlayer(struct PlayerInfo *player, int mode)
13121 {
13122   int jx = player->jx, jy = player->jy;
13123   int last_jx = player->last_jx, last_jy = player->last_jy;
13124   int move_stepsize = TILEX / player->move_delay_value;
13125
13126   if (!player->active)
13127     return;
13128
13129   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13130     return;
13131
13132   if (mode == SCROLL_INIT)
13133   {
13134     player->actual_frame_counter.count = FrameCounter;
13135     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13136
13137     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13138         Tile[last_jx][last_jy] == EL_EMPTY)
13139     {
13140       int last_field_block_delay = 0;   // start with no blocking at all
13141       int block_delay_adjustment = player->block_delay_adjustment;
13142
13143       // if player blocks last field, add delay for exactly one move
13144       if (player->block_last_field)
13145       {
13146         last_field_block_delay += player->move_delay_value;
13147
13148         // when blocking enabled, prevent moving up despite gravity
13149         if (player->gravity && player->MovDir == MV_UP)
13150           block_delay_adjustment = -1;
13151       }
13152
13153       // add block delay adjustment (also possible when not blocking)
13154       last_field_block_delay += block_delay_adjustment;
13155
13156       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13157       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13158     }
13159
13160     if (player->MovPos != 0)    // player has not yet reached destination
13161       return;
13162   }
13163   else if (!FrameReached(&player->actual_frame_counter))
13164     return;
13165
13166   if (player->MovPos != 0)
13167   {
13168     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13169     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13170
13171     // before DrawPlayer() to draw correct player graphic for this case
13172     if (player->MovPos == 0)
13173       CheckGravityMovement(player);
13174   }
13175
13176   if (player->MovPos == 0)      // player reached destination field
13177   {
13178     if (player->move_delay_reset_counter > 0)
13179     {
13180       player->move_delay_reset_counter--;
13181
13182       if (player->move_delay_reset_counter == 0)
13183       {
13184         // continue with normal speed after quickly moving through gate
13185         HALVE_PLAYER_SPEED(player);
13186
13187         // be able to make the next move without delay
13188         player->move_delay = 0;
13189       }
13190     }
13191
13192     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13193         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13194         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13195         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13196         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13197         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13198         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13199         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13200     {
13201       ExitPlayer(player);
13202
13203       if (game.players_still_needed == 0 &&
13204           (game.friends_still_needed == 0 ||
13205            IS_SP_ELEMENT(Tile[jx][jy])))
13206         LevelSolved();
13207     }
13208
13209     player->last_jx = jx;
13210     player->last_jy = jy;
13211
13212     // this breaks one level: "machine", level 000
13213     {
13214       int move_direction = player->MovDir;
13215       int enter_side = MV_DIR_OPPOSITE(move_direction);
13216       int leave_side = move_direction;
13217       int old_jx = last_jx;
13218       int old_jy = last_jy;
13219       int old_element = Tile[old_jx][old_jy];
13220       int new_element = Tile[jx][jy];
13221
13222       if (IS_CUSTOM_ELEMENT(old_element))
13223         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13224                                    CE_LEFT_BY_PLAYER,
13225                                    player->index_bit, leave_side);
13226
13227       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13228                                           CE_PLAYER_LEAVES_X,
13229                                           player->index_bit, leave_side);
13230
13231       // needed because pushed element has not yet reached its destination,
13232       // so it would trigger a change event at its previous field location
13233       if (!player->is_pushing)
13234       {
13235         if (IS_CUSTOM_ELEMENT(new_element))
13236           CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13237                                      player->index_bit, enter_side);
13238
13239         CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13240                                             CE_PLAYER_ENTERS_X,
13241                                             player->index_bit, enter_side);
13242       }
13243
13244       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13245                                         CE_MOVE_OF_X, move_direction);
13246     }
13247
13248     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13249     {
13250       TestIfPlayerTouchesBadThing(jx, jy);
13251       TestIfPlayerTouchesCustomElement(jx, jy);
13252
13253       // needed because pushed element has not yet reached its destination,
13254       // so it would trigger a change event at its previous field location
13255       if (!player->is_pushing)
13256         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13257
13258       if (level.finish_dig_collect &&
13259           (player->is_digging || player->is_collecting))
13260       {
13261         int last_element = player->last_removed_element;
13262         int move_direction = player->MovDir;
13263         int enter_side = MV_DIR_OPPOSITE(move_direction);
13264         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13265                             CE_PLAYER_COLLECTS_X);
13266
13267         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13268                                             player->index_bit, enter_side);
13269
13270         player->last_removed_element = EL_UNDEFINED;
13271       }
13272
13273       if (!player->active)
13274         RemovePlayer(player);
13275     }
13276
13277     if (level.use_step_counter)
13278       CheckLevelTime_StepCounter();
13279
13280     if (tape.single_step && tape.recording && !tape.pausing &&
13281         !player->programmed_action)
13282       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13283
13284     if (!player->programmed_action)
13285       CheckSaveEngineSnapshot(player);
13286   }
13287 }
13288
13289 void ScrollScreen(struct PlayerInfo *player, int mode)
13290 {
13291   static DelayCounter screen_frame_counter = { 0 };
13292
13293   if (mode == SCROLL_INIT)
13294   {
13295     // set scrolling step size according to actual player's moving speed
13296     ScrollStepSize = TILEX / player->move_delay_value;
13297
13298     screen_frame_counter.count = FrameCounter;
13299     screen_frame_counter.value = 1;
13300
13301     ScreenMovDir = player->MovDir;
13302     ScreenMovPos = player->MovPos;
13303     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13304     return;
13305   }
13306   else if (!FrameReached(&screen_frame_counter))
13307     return;
13308
13309   if (ScreenMovPos)
13310   {
13311     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13312     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13313     redraw_mask |= REDRAW_FIELD;
13314   }
13315   else
13316     ScreenMovDir = MV_NONE;
13317 }
13318
13319 void CheckNextToConditions(int x, int y)
13320 {
13321   int element = Tile[x][y];
13322
13323   if (IS_PLAYER(x, y))
13324     TestIfPlayerNextToCustomElement(x, y);
13325
13326   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13327       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13328     TestIfElementNextToCustomElement(x, y);
13329 }
13330
13331 void TestIfPlayerNextToCustomElement(int x, int y)
13332 {
13333   struct XY *xy = xy_topdown;
13334   static int trigger_sides[4][2] =
13335   {
13336     // center side       border side
13337     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13338     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13339     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13340     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13341   };
13342   int i;
13343
13344   if (!IS_PLAYER(x, y))
13345     return;
13346
13347   struct PlayerInfo *player = PLAYERINFO(x, y);
13348
13349   if (player->is_moving)
13350     return;
13351
13352   for (i = 0; i < NUM_DIRECTIONS; i++)
13353   {
13354     int xx = x + xy[i].x;
13355     int yy = y + xy[i].y;
13356     int border_side = trigger_sides[i][1];
13357     int border_element;
13358
13359     if (!IN_LEV_FIELD(xx, yy))
13360       continue;
13361
13362     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13363       continue;         // center and border element not connected
13364
13365     border_element = Tile[xx][yy];
13366
13367     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13368                                player->index_bit, border_side);
13369     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13370                                         CE_PLAYER_NEXT_TO_X,
13371                                         player->index_bit, border_side);
13372
13373     /* use player element that is initially defined in the level playfield,
13374        not the player element that corresponds to the runtime player number
13375        (example: a level that contains EL_PLAYER_3 as the only player would
13376        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13377
13378     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13379                              CE_NEXT_TO_X, border_side);
13380   }
13381 }
13382
13383 void TestIfPlayerTouchesCustomElement(int x, int y)
13384 {
13385   struct XY *xy = xy_topdown;
13386   static int trigger_sides[4][2] =
13387   {
13388     // center side       border side
13389     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13390     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13391     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13392     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13393   };
13394   static int touch_dir[4] =
13395   {
13396     MV_LEFT | MV_RIGHT,
13397     MV_UP   | MV_DOWN,
13398     MV_UP   | MV_DOWN,
13399     MV_LEFT | MV_RIGHT
13400   };
13401   int center_element = Tile[x][y];      // should always be non-moving!
13402   int i;
13403
13404   for (i = 0; i < NUM_DIRECTIONS; i++)
13405   {
13406     int xx = x + xy[i].x;
13407     int yy = y + xy[i].y;
13408     int center_side = trigger_sides[i][0];
13409     int border_side = trigger_sides[i][1];
13410     int border_element;
13411
13412     if (!IN_LEV_FIELD(xx, yy))
13413       continue;
13414
13415     if (IS_PLAYER(x, y))                // player found at center element
13416     {
13417       struct PlayerInfo *player = PLAYERINFO(x, y);
13418
13419       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13420         border_element = Tile[xx][yy];          // may be moving!
13421       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13422         border_element = Tile[xx][yy];
13423       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13424         border_element = MovingOrBlocked2Element(xx, yy);
13425       else
13426         continue;               // center and border element do not touch
13427
13428       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13429                                  player->index_bit, border_side);
13430       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13431                                           CE_PLAYER_TOUCHES_X,
13432                                           player->index_bit, border_side);
13433
13434       {
13435         /* use player element that is initially defined in the level playfield,
13436            not the player element that corresponds to the runtime player number
13437            (example: a level that contains EL_PLAYER_3 as the only player would
13438            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13439         int player_element = PLAYERINFO(x, y)->initial_element;
13440
13441         CheckElementChangeBySide(xx, yy, border_element, player_element,
13442                                  CE_TOUCHING_X, border_side);
13443       }
13444     }
13445     else if (IS_PLAYER(xx, yy))         // player found at border element
13446     {
13447       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13448
13449       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13450       {
13451         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13452           continue;             // center and border element do not touch
13453       }
13454
13455       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13456                                  player->index_bit, center_side);
13457       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13458                                           CE_PLAYER_TOUCHES_X,
13459                                           player->index_bit, center_side);
13460
13461       {
13462         /* use player element that is initially defined in the level playfield,
13463            not the player element that corresponds to the runtime player number
13464            (example: a level that contains EL_PLAYER_3 as the only player would
13465            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13466         int player_element = PLAYERINFO(xx, yy)->initial_element;
13467
13468         CheckElementChangeBySide(x, y, center_element, player_element,
13469                                  CE_TOUCHING_X, center_side);
13470       }
13471
13472       break;
13473     }
13474   }
13475 }
13476
13477 void TestIfElementNextToCustomElement(int x, int y)
13478 {
13479   struct XY *xy = xy_topdown;
13480   static int trigger_sides[4][2] =
13481   {
13482     // center side      border side
13483     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13484     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13485     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13486     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13487   };
13488   int center_element = Tile[x][y];      // should always be non-moving!
13489   int i;
13490
13491   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13492     return;
13493
13494   for (i = 0; i < NUM_DIRECTIONS; i++)
13495   {
13496     int xx = x + xy[i].x;
13497     int yy = y + xy[i].y;
13498     int border_side = trigger_sides[i][1];
13499     int border_element;
13500
13501     if (!IN_LEV_FIELD(xx, yy))
13502       continue;
13503
13504     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13505       continue;                 // center and border element not connected
13506
13507     border_element = Tile[xx][yy];
13508
13509     // check for change of center element (but change it only once)
13510     if (CheckElementChangeBySide(x, y, center_element, border_element,
13511                                  CE_NEXT_TO_X, border_side))
13512       break;
13513   }
13514 }
13515
13516 void TestIfElementTouchesCustomElement(int x, int y)
13517 {
13518   struct XY *xy = xy_topdown;
13519   static int trigger_sides[4][2] =
13520   {
13521     // center side      border side
13522     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13523     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13524     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13525     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13526   };
13527   static int touch_dir[4] =
13528   {
13529     MV_LEFT | MV_RIGHT,
13530     MV_UP   | MV_DOWN,
13531     MV_UP   | MV_DOWN,
13532     MV_LEFT | MV_RIGHT
13533   };
13534   boolean change_center_element = FALSE;
13535   int center_element = Tile[x][y];      // should always be non-moving!
13536   int border_element_old[NUM_DIRECTIONS];
13537   int i;
13538
13539   for (i = 0; i < NUM_DIRECTIONS; i++)
13540   {
13541     int xx = x + xy[i].x;
13542     int yy = y + xy[i].y;
13543     int border_element;
13544
13545     border_element_old[i] = -1;
13546
13547     if (!IN_LEV_FIELD(xx, yy))
13548       continue;
13549
13550     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13551       border_element = Tile[xx][yy];    // may be moving!
13552     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13553       border_element = Tile[xx][yy];
13554     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13555       border_element = MovingOrBlocked2Element(xx, yy);
13556     else
13557       continue;                 // center and border element do not touch
13558
13559     border_element_old[i] = border_element;
13560   }
13561
13562   for (i = 0; i < NUM_DIRECTIONS; i++)
13563   {
13564     int xx = x + xy[i].x;
13565     int yy = y + xy[i].y;
13566     int center_side = trigger_sides[i][0];
13567     int border_element = border_element_old[i];
13568
13569     if (border_element == -1)
13570       continue;
13571
13572     // check for change of border element
13573     CheckElementChangeBySide(xx, yy, border_element, center_element,
13574                              CE_TOUCHING_X, center_side);
13575
13576     // (center element cannot be player, so we dont have to check this here)
13577   }
13578
13579   for (i = 0; i < NUM_DIRECTIONS; i++)
13580   {
13581     int xx = x + xy[i].x;
13582     int yy = y + xy[i].y;
13583     int border_side = trigger_sides[i][1];
13584     int border_element = border_element_old[i];
13585
13586     if (border_element == -1)
13587       continue;
13588
13589     // check for change of center element (but change it only once)
13590     if (!change_center_element)
13591       change_center_element =
13592         CheckElementChangeBySide(x, y, center_element, border_element,
13593                                  CE_TOUCHING_X, border_side);
13594
13595     if (IS_PLAYER(xx, yy))
13596     {
13597       /* use player element that is initially defined in the level playfield,
13598          not the player element that corresponds to the runtime player number
13599          (example: a level that contains EL_PLAYER_3 as the only player would
13600          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13601       int player_element = PLAYERINFO(xx, yy)->initial_element;
13602
13603       CheckElementChangeBySide(x, y, center_element, player_element,
13604                                CE_TOUCHING_X, border_side);
13605     }
13606   }
13607 }
13608
13609 void TestIfElementHitsCustomElement(int x, int y, int direction)
13610 {
13611   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13612   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13613   int hitx = x + dx, hity = y + dy;
13614   int hitting_element = Tile[x][y];
13615   int touched_element;
13616
13617   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13618     return;
13619
13620   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13621                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13622
13623   if (IN_LEV_FIELD(hitx, hity))
13624   {
13625     int opposite_direction = MV_DIR_OPPOSITE(direction);
13626     int hitting_side = direction;
13627     int touched_side = opposite_direction;
13628     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13629                           MovDir[hitx][hity] != direction ||
13630                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13631
13632     object_hit = TRUE;
13633
13634     if (object_hit)
13635     {
13636       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13637                                CE_HITTING_X, touched_side);
13638
13639       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13640                                CE_HIT_BY_X, hitting_side);
13641
13642       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13643                                CE_HIT_BY_SOMETHING, opposite_direction);
13644
13645       if (IS_PLAYER(hitx, hity))
13646       {
13647         /* use player element that is initially defined in the level playfield,
13648            not the player element that corresponds to the runtime player number
13649            (example: a level that contains EL_PLAYER_3 as the only player would
13650            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13651         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13652
13653         CheckElementChangeBySide(x, y, hitting_element, player_element,
13654                                  CE_HITTING_X, touched_side);
13655       }
13656     }
13657   }
13658
13659   // "hitting something" is also true when hitting the playfield border
13660   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13661                            CE_HITTING_SOMETHING, direction);
13662 }
13663
13664 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13665 {
13666   int i, kill_x = -1, kill_y = -1;
13667
13668   int bad_element = -1;
13669   struct XY *test_xy = xy_topdown;
13670   static int test_dir[4] =
13671   {
13672     MV_UP,
13673     MV_LEFT,
13674     MV_RIGHT,
13675     MV_DOWN
13676   };
13677
13678   for (i = 0; i < NUM_DIRECTIONS; i++)
13679   {
13680     int test_x, test_y, test_move_dir, test_element;
13681
13682     test_x = good_x + test_xy[i].x;
13683     test_y = good_y + test_xy[i].y;
13684
13685     if (!IN_LEV_FIELD(test_x, test_y))
13686       continue;
13687
13688     test_move_dir =
13689       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13690
13691     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13692
13693     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13694        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13695     */
13696     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13697         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13698     {
13699       kill_x = test_x;
13700       kill_y = test_y;
13701       bad_element = test_element;
13702
13703       break;
13704     }
13705   }
13706
13707   if (kill_x != -1 || kill_y != -1)
13708   {
13709     if (IS_PLAYER(good_x, good_y))
13710     {
13711       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13712
13713       if (player->shield_deadly_time_left > 0 &&
13714           !IS_INDESTRUCTIBLE(bad_element))
13715         Bang(kill_x, kill_y);
13716       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13717         KillPlayer(player);
13718     }
13719     else
13720       Bang(good_x, good_y);
13721   }
13722 }
13723
13724 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13725 {
13726   int i, kill_x = -1, kill_y = -1;
13727   int bad_element = Tile[bad_x][bad_y];
13728   struct XY *test_xy = xy_topdown;
13729   static int touch_dir[4] =
13730   {
13731     MV_LEFT | MV_RIGHT,
13732     MV_UP   | MV_DOWN,
13733     MV_UP   | MV_DOWN,
13734     MV_LEFT | MV_RIGHT
13735   };
13736   static int test_dir[4] =
13737   {
13738     MV_UP,
13739     MV_LEFT,
13740     MV_RIGHT,
13741     MV_DOWN
13742   };
13743
13744   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13745     return;
13746
13747   for (i = 0; i < NUM_DIRECTIONS; i++)
13748   {
13749     int test_x, test_y, test_move_dir, test_element;
13750
13751     test_x = bad_x + test_xy[i].x;
13752     test_y = bad_y + test_xy[i].y;
13753
13754     if (!IN_LEV_FIELD(test_x, test_y))
13755       continue;
13756
13757     test_move_dir =
13758       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13759
13760     test_element = Tile[test_x][test_y];
13761
13762     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13763        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13764     */
13765     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13766         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13767     {
13768       // good thing is player or penguin that does not move away
13769       if (IS_PLAYER(test_x, test_y))
13770       {
13771         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13772
13773         if (bad_element == EL_ROBOT && player->is_moving)
13774           continue;     // robot does not kill player if he is moving
13775
13776         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13777         {
13778           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13779             continue;           // center and border element do not touch
13780         }
13781
13782         kill_x = test_x;
13783         kill_y = test_y;
13784
13785         break;
13786       }
13787       else if (test_element == EL_PENGUIN)
13788       {
13789         kill_x = test_x;
13790         kill_y = test_y;
13791
13792         break;
13793       }
13794     }
13795   }
13796
13797   if (kill_x != -1 || kill_y != -1)
13798   {
13799     if (IS_PLAYER(kill_x, kill_y))
13800     {
13801       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13802
13803       if (player->shield_deadly_time_left > 0 &&
13804           !IS_INDESTRUCTIBLE(bad_element))
13805         Bang(bad_x, bad_y);
13806       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13807         KillPlayer(player);
13808     }
13809     else
13810       Bang(kill_x, kill_y);
13811   }
13812 }
13813
13814 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13815 {
13816   int bad_element = Tile[bad_x][bad_y];
13817   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13818   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13819   int test_x = bad_x + dx, test_y = bad_y + dy;
13820   int test_move_dir, test_element;
13821   int kill_x = -1, kill_y = -1;
13822
13823   if (!IN_LEV_FIELD(test_x, test_y))
13824     return;
13825
13826   test_move_dir =
13827     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13828
13829   test_element = Tile[test_x][test_y];
13830
13831   if (test_move_dir != bad_move_dir)
13832   {
13833     // good thing can be player or penguin that does not move away
13834     if (IS_PLAYER(test_x, test_y))
13835     {
13836       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13837
13838       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13839          player as being hit when he is moving towards the bad thing, because
13840          the "get hit by" condition would be lost after the player stops) */
13841       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13842         return;         // player moves away from bad thing
13843
13844       kill_x = test_x;
13845       kill_y = test_y;
13846     }
13847     else if (test_element == EL_PENGUIN)
13848     {
13849       kill_x = test_x;
13850       kill_y = test_y;
13851     }
13852   }
13853
13854   if (kill_x != -1 || kill_y != -1)
13855   {
13856     if (IS_PLAYER(kill_x, kill_y))
13857     {
13858       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13859
13860       if (player->shield_deadly_time_left > 0 &&
13861           !IS_INDESTRUCTIBLE(bad_element))
13862         Bang(bad_x, bad_y);
13863       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13864         KillPlayer(player);
13865     }
13866     else
13867       Bang(kill_x, kill_y);
13868   }
13869 }
13870
13871 void TestIfPlayerTouchesBadThing(int x, int y)
13872 {
13873   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13874 }
13875
13876 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13877 {
13878   TestIfGoodThingHitsBadThing(x, y, move_dir);
13879 }
13880
13881 void TestIfBadThingTouchesPlayer(int x, int y)
13882 {
13883   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13884 }
13885
13886 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13887 {
13888   TestIfBadThingHitsGoodThing(x, y, move_dir);
13889 }
13890
13891 void TestIfFriendTouchesBadThing(int x, int y)
13892 {
13893   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13894 }
13895
13896 void TestIfBadThingTouchesFriend(int x, int y)
13897 {
13898   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13899 }
13900
13901 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13902 {
13903   int i, kill_x = bad_x, kill_y = bad_y;
13904   struct XY *xy = xy_topdown;
13905
13906   for (i = 0; i < NUM_DIRECTIONS; i++)
13907   {
13908     int x, y, element;
13909
13910     x = bad_x + xy[i].x;
13911     y = bad_y + xy[i].y;
13912     if (!IN_LEV_FIELD(x, y))
13913       continue;
13914
13915     element = Tile[x][y];
13916     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13917         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13918     {
13919       kill_x = x;
13920       kill_y = y;
13921       break;
13922     }
13923   }
13924
13925   if (kill_x != bad_x || kill_y != bad_y)
13926     Bang(bad_x, bad_y);
13927 }
13928
13929 void KillPlayer(struct PlayerInfo *player)
13930 {
13931   int jx = player->jx, jy = player->jy;
13932
13933   if (!player->active)
13934     return;
13935
13936 #if 0
13937   Debug("game:playing:KillPlayer",
13938         "0: killed == %d, active == %d, reanimated == %d",
13939         player->killed, player->active, player->reanimated);
13940 #endif
13941
13942   /* the following code was introduced to prevent an infinite loop when calling
13943      -> Bang()
13944      -> CheckTriggeredElementChangeExt()
13945      -> ExecuteCustomElementAction()
13946      -> KillPlayer()
13947      -> (infinitely repeating the above sequence of function calls)
13948      which occurs when killing the player while having a CE with the setting
13949      "kill player X when explosion of <player X>"; the solution using a new
13950      field "player->killed" was chosen for backwards compatibility, although
13951      clever use of the fields "player->active" etc. would probably also work */
13952 #if 1
13953   if (player->killed)
13954     return;
13955 #endif
13956
13957   player->killed = TRUE;
13958
13959   // remove accessible field at the player's position
13960   RemoveField(jx, jy);
13961
13962   // deactivate shield (else Bang()/Explode() would not work right)
13963   player->shield_normal_time_left = 0;
13964   player->shield_deadly_time_left = 0;
13965
13966 #if 0
13967   Debug("game:playing:KillPlayer",
13968         "1: killed == %d, active == %d, reanimated == %d",
13969         player->killed, player->active, player->reanimated);
13970 #endif
13971
13972   Bang(jx, jy);
13973
13974 #if 0
13975   Debug("game:playing:KillPlayer",
13976         "2: killed == %d, active == %d, reanimated == %d",
13977         player->killed, player->active, player->reanimated);
13978 #endif
13979
13980   if (player->reanimated)       // killed player may have been reanimated
13981     player->killed = player->reanimated = FALSE;
13982   else
13983     BuryPlayer(player);
13984 }
13985
13986 static void KillPlayerUnlessEnemyProtected(int x, int y)
13987 {
13988   if (!PLAYER_ENEMY_PROTECTED(x, y))
13989     KillPlayer(PLAYERINFO(x, y));
13990 }
13991
13992 static void KillPlayerUnlessExplosionProtected(int x, int y)
13993 {
13994   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13995     KillPlayer(PLAYERINFO(x, y));
13996 }
13997
13998 void BuryPlayer(struct PlayerInfo *player)
13999 {
14000   int jx = player->jx, jy = player->jy;
14001
14002   if (!player->active)
14003     return;
14004
14005   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14006
14007   RemovePlayer(player);
14008
14009   player->buried = TRUE;
14010
14011   if (game.all_players_gone)
14012     game.GameOver = TRUE;
14013 }
14014
14015 void RemovePlayer(struct PlayerInfo *player)
14016 {
14017   int jx = player->jx, jy = player->jy;
14018   int i, found = FALSE;
14019
14020   player->present = FALSE;
14021   player->active = FALSE;
14022
14023   // required for some CE actions (even if the player is not active anymore)
14024   player->MovPos = 0;
14025
14026   if (!ExplodeField[jx][jy])
14027     StorePlayer[jx][jy] = 0;
14028
14029   if (player->is_moving)
14030     TEST_DrawLevelField(player->last_jx, player->last_jy);
14031
14032   for (i = 0; i < MAX_PLAYERS; i++)
14033     if (stored_player[i].active)
14034       found = TRUE;
14035
14036   if (!found)
14037   {
14038     game.all_players_gone = TRUE;
14039     game.GameOver = TRUE;
14040   }
14041
14042   game.exit_x = game.robot_wheel_x = jx;
14043   game.exit_y = game.robot_wheel_y = jy;
14044 }
14045
14046 void ExitPlayer(struct PlayerInfo *player)
14047 {
14048   DrawPlayer(player);   // needed here only to cleanup last field
14049   RemovePlayer(player);
14050
14051   if (game.players_still_needed > 0)
14052     game.players_still_needed--;
14053 }
14054
14055 static void SetFieldForSnapping(int x, int y, int element, int direction,
14056                                 int player_index_bit)
14057 {
14058   struct ElementInfo *ei = &element_info[element];
14059   int direction_bit = MV_DIR_TO_BIT(direction);
14060   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14061   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14062                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14063
14064   Tile[x][y] = EL_ELEMENT_SNAPPING;
14065   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14066   MovDir[x][y] = direction;
14067   Store[x][y] = element;
14068   Store2[x][y] = player_index_bit;
14069
14070   ResetGfxAnimation(x, y);
14071
14072   GfxElement[x][y] = element;
14073   GfxAction[x][y] = action;
14074   GfxDir[x][y] = direction;
14075   GfxFrame[x][y] = -1;
14076 }
14077
14078 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14079                                    int player_index_bit)
14080 {
14081   TestIfElementTouchesCustomElement(x, y);      // for empty space
14082
14083   if (level.finish_dig_collect)
14084   {
14085     int dig_side = MV_DIR_OPPOSITE(direction);
14086     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14087                         CE_PLAYER_COLLECTS_X);
14088
14089     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14090                                         player_index_bit, dig_side);
14091     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14092                                         player_index_bit, dig_side);
14093   }
14094 }
14095
14096 /*
14097   =============================================================================
14098   checkDiagonalPushing()
14099   -----------------------------------------------------------------------------
14100   check if diagonal input device direction results in pushing of object
14101   (by checking if the alternative direction is walkable, diggable, ...)
14102   =============================================================================
14103 */
14104
14105 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14106                                     int x, int y, int real_dx, int real_dy)
14107 {
14108   int jx, jy, dx, dy, xx, yy;
14109
14110   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14111     return TRUE;
14112
14113   // diagonal direction: check alternative direction
14114   jx = player->jx;
14115   jy = player->jy;
14116   dx = x - jx;
14117   dy = y - jy;
14118   xx = jx + (dx == 0 ? real_dx : 0);
14119   yy = jy + (dy == 0 ? real_dy : 0);
14120
14121   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14122 }
14123
14124 /*
14125   =============================================================================
14126   DigField()
14127   -----------------------------------------------------------------------------
14128   x, y:                 field next to player (non-diagonal) to try to dig to
14129   real_dx, real_dy:     direction as read from input device (can be diagonal)
14130   =============================================================================
14131 */
14132
14133 static int DigField(struct PlayerInfo *player,
14134                     int oldx, int oldy, int x, int y,
14135                     int real_dx, int real_dy, int mode)
14136 {
14137   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14138   boolean player_was_pushing = player->is_pushing;
14139   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14140   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14141   int jx = oldx, jy = oldy;
14142   int dx = x - jx, dy = y - jy;
14143   int nextx = x + dx, nexty = y + dy;
14144   int move_direction = (dx == -1 ? MV_LEFT  :
14145                         dx == +1 ? MV_RIGHT :
14146                         dy == -1 ? MV_UP    :
14147                         dy == +1 ? MV_DOWN  : MV_NONE);
14148   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14149   int dig_side = MV_DIR_OPPOSITE(move_direction);
14150   int old_element = Tile[jx][jy];
14151   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14152   int collect_count;
14153
14154   if (is_player)                // function can also be called by EL_PENGUIN
14155   {
14156     if (player->MovPos == 0)
14157     {
14158       player->is_digging = FALSE;
14159       player->is_collecting = FALSE;
14160     }
14161
14162     if (player->MovPos == 0)    // last pushing move finished
14163       player->is_pushing = FALSE;
14164
14165     if (mode == DF_NO_PUSH)     // player just stopped pushing
14166     {
14167       player->is_switching = FALSE;
14168       player->push_delay = -1;
14169
14170       return MP_NO_ACTION;
14171     }
14172   }
14173   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14174     old_element = Back[jx][jy];
14175
14176   // in case of element dropped at player position, check background
14177   else if (Back[jx][jy] != EL_EMPTY &&
14178            game.engine_version >= VERSION_IDENT(2,2,0,0))
14179     old_element = Back[jx][jy];
14180
14181   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14182     return MP_NO_ACTION;        // field has no opening in this direction
14183
14184   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element, opposite_direction))
14185     return MP_NO_ACTION;        // field has no opening in this direction
14186
14187   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14188   {
14189     SplashAcid(x, y);
14190
14191     Tile[jx][jy] = player->artwork_element;
14192     InitMovingField(jx, jy, MV_DOWN);
14193     Store[jx][jy] = EL_ACID;
14194     ContinueMoving(jx, jy);
14195     BuryPlayer(player);
14196
14197     return MP_DONT_RUN_INTO;
14198   }
14199
14200   if (player_can_move && DONT_RUN_INTO(element))
14201   {
14202     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14203
14204     return MP_DONT_RUN_INTO;
14205   }
14206
14207   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14208     return MP_NO_ACTION;
14209
14210   collect_count = element_info[element].collect_count_initial;
14211
14212   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14213     return MP_NO_ACTION;
14214
14215   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14216     player_can_move = player_can_move_or_snap;
14217
14218   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14219       game.engine_version >= VERSION_IDENT(2,2,0,0))
14220   {
14221     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14222                                player->index_bit, dig_side);
14223     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14224                                         player->index_bit, dig_side);
14225
14226     if (element == EL_DC_LANDMINE)
14227       Bang(x, y);
14228
14229     if (Tile[x][y] != element)          // field changed by snapping
14230       return MP_ACTION;
14231
14232     return MP_NO_ACTION;
14233   }
14234
14235   if (player->gravity && is_player && !player->is_auto_moving &&
14236       canFallDown(player) && move_direction != MV_DOWN &&
14237       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14238     return MP_NO_ACTION;        // player cannot walk here due to gravity
14239
14240   if (player_can_move &&
14241       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14242   {
14243     int sound_element = SND_ELEMENT(element);
14244     int sound_action = ACTION_WALKING;
14245
14246     if (IS_RND_GATE(element))
14247     {
14248       if (!player->key[RND_GATE_NR(element)])
14249         return MP_NO_ACTION;
14250     }
14251     else if (IS_RND_GATE_GRAY(element))
14252     {
14253       if (!player->key[RND_GATE_GRAY_NR(element)])
14254         return MP_NO_ACTION;
14255     }
14256     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14257     {
14258       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14259         return MP_NO_ACTION;
14260     }
14261     else if (element == EL_EXIT_OPEN ||
14262              element == EL_EM_EXIT_OPEN ||
14263              element == EL_EM_EXIT_OPENING ||
14264              element == EL_STEEL_EXIT_OPEN ||
14265              element == EL_EM_STEEL_EXIT_OPEN ||
14266              element == EL_EM_STEEL_EXIT_OPENING ||
14267              element == EL_SP_EXIT_OPEN ||
14268              element == EL_SP_EXIT_OPENING)
14269     {
14270       sound_action = ACTION_PASSING;    // player is passing exit
14271     }
14272     else if (element == EL_EMPTY)
14273     {
14274       sound_action = ACTION_MOVING;             // nothing to walk on
14275     }
14276
14277     // play sound from background or player, whatever is available
14278     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14279       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14280     else
14281       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14282   }
14283   else if (player_can_move &&
14284            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14285   {
14286     if (!ACCESS_FROM(element, opposite_direction))
14287       return MP_NO_ACTION;      // field not accessible from this direction
14288
14289     if (CAN_MOVE(element))      // only fixed elements can be passed!
14290       return MP_NO_ACTION;
14291
14292     if (IS_EM_GATE(element))
14293     {
14294       if (!player->key[EM_GATE_NR(element)])
14295         return MP_NO_ACTION;
14296     }
14297     else if (IS_EM_GATE_GRAY(element))
14298     {
14299       if (!player->key[EM_GATE_GRAY_NR(element)])
14300         return MP_NO_ACTION;
14301     }
14302     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14303     {
14304       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14305         return MP_NO_ACTION;
14306     }
14307     else if (IS_EMC_GATE(element))
14308     {
14309       if (!player->key[EMC_GATE_NR(element)])
14310         return MP_NO_ACTION;
14311     }
14312     else if (IS_EMC_GATE_GRAY(element))
14313     {
14314       if (!player->key[EMC_GATE_GRAY_NR(element)])
14315         return MP_NO_ACTION;
14316     }
14317     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14318     {
14319       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14320         return MP_NO_ACTION;
14321     }
14322     else if (element == EL_DC_GATE_WHITE ||
14323              element == EL_DC_GATE_WHITE_GRAY ||
14324              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14325     {
14326       if (player->num_white_keys == 0)
14327         return MP_NO_ACTION;
14328
14329       player->num_white_keys--;
14330     }
14331     else if (IS_SP_PORT(element))
14332     {
14333       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14334           element == EL_SP_GRAVITY_PORT_RIGHT ||
14335           element == EL_SP_GRAVITY_PORT_UP ||
14336           element == EL_SP_GRAVITY_PORT_DOWN)
14337         player->gravity = !player->gravity;
14338       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14339                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14340                element == EL_SP_GRAVITY_ON_PORT_UP ||
14341                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14342         player->gravity = TRUE;
14343       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14344                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14345                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14346                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14347         player->gravity = FALSE;
14348     }
14349
14350     // automatically move to the next field with double speed
14351     player->programmed_action = move_direction;
14352
14353     if (player->move_delay_reset_counter == 0)
14354     {
14355       player->move_delay_reset_counter = 2;     // two double speed steps
14356
14357       DOUBLE_PLAYER_SPEED(player);
14358     }
14359
14360     PlayLevelSoundAction(x, y, ACTION_PASSING);
14361   }
14362   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14363   {
14364     RemoveField(x, y);
14365
14366     if (mode != DF_SNAP)
14367     {
14368       GfxElement[x][y] = GFX_ELEMENT(element);
14369       player->is_digging = TRUE;
14370     }
14371
14372     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14373
14374     // use old behaviour for old levels (digging)
14375     if (!level.finish_dig_collect)
14376     {
14377       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14378                                           player->index_bit, dig_side);
14379
14380       // if digging triggered player relocation, finish digging tile
14381       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14382         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14383     }
14384
14385     if (mode == DF_SNAP)
14386     {
14387       if (level.block_snap_field)
14388         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14389       else
14390         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14391
14392       // use old behaviour for old levels (snapping)
14393       if (!level.finish_dig_collect)
14394         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14395                                             player->index_bit, dig_side);
14396     }
14397   }
14398   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14399   {
14400     RemoveField(x, y);
14401
14402     if (is_player && mode != DF_SNAP)
14403     {
14404       GfxElement[x][y] = element;
14405       player->is_collecting = TRUE;
14406     }
14407
14408     if (element == EL_SPEED_PILL)
14409     {
14410       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14411     }
14412     else if (element == EL_EXTRA_TIME && level.time > 0)
14413     {
14414       TimeLeft += level.extra_time;
14415
14416       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14417
14418       DisplayGameControlValues();
14419     }
14420     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14421     {
14422       int shield_time = (element == EL_SHIELD_DEADLY ?
14423                          level.shield_deadly_time :
14424                          level.shield_normal_time);
14425
14426       player->shield_normal_time_left += shield_time;
14427       if (element == EL_SHIELD_DEADLY)
14428         player->shield_deadly_time_left += shield_time;
14429     }
14430     else if (element == EL_DYNAMITE ||
14431              element == EL_EM_DYNAMITE ||
14432              element == EL_SP_DISK_RED)
14433     {
14434       if (player->inventory_size < MAX_INVENTORY_SIZE)
14435         player->inventory_element[player->inventory_size++] = element;
14436
14437       DrawGameDoorValues();
14438     }
14439     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14440     {
14441       player->dynabomb_count++;
14442       player->dynabombs_left++;
14443     }
14444     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14445     {
14446       player->dynabomb_size++;
14447     }
14448     else if (element == EL_DYNABOMB_INCREASE_POWER)
14449     {
14450       player->dynabomb_xl = TRUE;
14451     }
14452     else if (IS_KEY(element))
14453     {
14454       player->key[KEY_NR(element)] = TRUE;
14455
14456       DrawGameDoorValues();
14457     }
14458     else if (element == EL_DC_KEY_WHITE)
14459     {
14460       player->num_white_keys++;
14461
14462       // display white keys?
14463       // DrawGameDoorValues();
14464     }
14465     else if (IS_ENVELOPE(element))
14466     {
14467       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14468
14469       if (!wait_for_snapping)
14470         player->show_envelope = element;
14471     }
14472     else if (element == EL_EMC_LENSES)
14473     {
14474       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14475
14476       RedrawAllInvisibleElementsForLenses();
14477     }
14478     else if (element == EL_EMC_MAGNIFIER)
14479     {
14480       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14481
14482       RedrawAllInvisibleElementsForMagnifier();
14483     }
14484     else if (IS_DROPPABLE(element) ||
14485              IS_THROWABLE(element))     // can be collected and dropped
14486     {
14487       int i;
14488
14489       if (collect_count == 0)
14490         player->inventory_infinite_element = element;
14491       else
14492         for (i = 0; i < collect_count; i++)
14493           if (player->inventory_size < MAX_INVENTORY_SIZE)
14494             player->inventory_element[player->inventory_size++] = element;
14495
14496       DrawGameDoorValues();
14497     }
14498     else if (collect_count > 0)
14499     {
14500       game.gems_still_needed -= collect_count;
14501       if (game.gems_still_needed < 0)
14502         game.gems_still_needed = 0;
14503
14504       game.snapshot.collected_item = TRUE;
14505
14506       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14507
14508       DisplayGameControlValues();
14509     }
14510
14511     RaiseScoreElement(element);
14512     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14513
14514     // use old behaviour for old levels (collecting)
14515     if (!level.finish_dig_collect && is_player)
14516     {
14517       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14518                                           player->index_bit, dig_side);
14519
14520       // if collecting triggered player relocation, finish collecting tile
14521       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14522         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14523     }
14524
14525     if (mode == DF_SNAP)
14526     {
14527       if (level.block_snap_field)
14528         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14529       else
14530         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14531
14532       // use old behaviour for old levels (snapping)
14533       if (!level.finish_dig_collect)
14534         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14535                                             player->index_bit, dig_side);
14536     }
14537   }
14538   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14539   {
14540     if (mode == DF_SNAP && element != EL_BD_ROCK)
14541       return MP_NO_ACTION;
14542
14543     if (CAN_FALL(element) && dy)
14544       return MP_NO_ACTION;
14545
14546     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14547         !(element == EL_SPRING && level.use_spring_bug))
14548       return MP_NO_ACTION;
14549
14550     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14551         ((move_direction & MV_VERTICAL &&
14552           ((element_info[element].move_pattern & MV_LEFT &&
14553             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14554            (element_info[element].move_pattern & MV_RIGHT &&
14555             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14556          (move_direction & MV_HORIZONTAL &&
14557           ((element_info[element].move_pattern & MV_UP &&
14558             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14559            (element_info[element].move_pattern & MV_DOWN &&
14560             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14561       return MP_NO_ACTION;
14562
14563     // do not push elements already moving away faster than player
14564     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14565         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14566       return MP_NO_ACTION;
14567
14568     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14569     {
14570       if (player->push_delay_value == -1 || !player_was_pushing)
14571         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14572     }
14573     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14574     {
14575       if (player->push_delay_value == -1)
14576         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14577     }
14578     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14579     {
14580       if (!player->is_pushing)
14581         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14582     }
14583
14584     player->is_pushing = TRUE;
14585     player->is_active = TRUE;
14586
14587     if (!(IN_LEV_FIELD(nextx, nexty) &&
14588           (IS_FREE(nextx, nexty) ||
14589            (IS_SB_ELEMENT(element) &&
14590             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14591            (IS_CUSTOM_ELEMENT(element) &&
14592             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14593       return MP_NO_ACTION;
14594
14595     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14596       return MP_NO_ACTION;
14597
14598     if (player->push_delay == -1)       // new pushing; restart delay
14599       player->push_delay = 0;
14600
14601     if (player->push_delay < player->push_delay_value &&
14602         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14603         element != EL_SPRING && element != EL_BALLOON)
14604     {
14605       // make sure that there is no move delay before next try to push
14606       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14607         player->move_delay = 0;
14608
14609       return MP_NO_ACTION;
14610     }
14611
14612     if (IS_CUSTOM_ELEMENT(element) &&
14613         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14614     {
14615       if (!DigFieldByCE(nextx, nexty, element))
14616         return MP_NO_ACTION;
14617     }
14618
14619     if (IS_SB_ELEMENT(element))
14620     {
14621       boolean sokoban_task_solved = FALSE;
14622
14623       if (element == EL_SOKOBAN_FIELD_FULL)
14624       {
14625         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14626
14627         IncrementSokobanFieldsNeeded();
14628         IncrementSokobanObjectsNeeded();
14629       }
14630
14631       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14632       {
14633         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14634
14635         DecrementSokobanFieldsNeeded();
14636         DecrementSokobanObjectsNeeded();
14637
14638         // sokoban object was pushed from empty field to sokoban field
14639         if (Back[x][y] == EL_EMPTY)
14640           sokoban_task_solved = TRUE;
14641       }
14642
14643       Tile[x][y] = EL_SOKOBAN_OBJECT;
14644
14645       if (Back[x][y] == Back[nextx][nexty])
14646         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14647       else if (Back[x][y] != 0)
14648         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14649                                     ACTION_EMPTYING);
14650       else
14651         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14652                                     ACTION_FILLING);
14653
14654       if (sokoban_task_solved &&
14655           game.sokoban_fields_still_needed == 0 &&
14656           game.sokoban_objects_still_needed == 0 &&
14657           level.auto_exit_sokoban)
14658       {
14659         game.players_still_needed = 0;
14660
14661         LevelSolved();
14662
14663         PlaySound(SND_GAME_SOKOBAN_SOLVING);
14664       }
14665     }
14666     else
14667       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14668
14669     InitMovingField(x, y, move_direction);
14670     GfxAction[x][y] = ACTION_PUSHING;
14671
14672     if (mode == DF_SNAP)
14673       ContinueMoving(x, y);
14674     else
14675       MovPos[x][y] = (dx != 0 ? dx : dy);
14676
14677     Pushed[x][y] = TRUE;
14678     Pushed[nextx][nexty] = TRUE;
14679
14680     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14681       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14682     else
14683       player->push_delay_value = -1;    // get new value later
14684
14685     // check for element change _after_ element has been pushed
14686     if (game.use_change_when_pushing_bug)
14687     {
14688       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14689                                  player->index_bit, dig_side);
14690       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14691                                           player->index_bit, dig_side);
14692     }
14693   }
14694   else if (IS_SWITCHABLE(element))
14695   {
14696     if (PLAYER_SWITCHING(player, x, y))
14697     {
14698       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14699                                           player->index_bit, dig_side);
14700
14701       return MP_ACTION;
14702     }
14703
14704     player->is_switching = TRUE;
14705     player->switch_x = x;
14706     player->switch_y = y;
14707
14708     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14709
14710     if (element == EL_ROBOT_WHEEL)
14711     {
14712       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14713
14714       game.robot_wheel_x = x;
14715       game.robot_wheel_y = y;
14716       game.robot_wheel_active = TRUE;
14717
14718       TEST_DrawLevelField(x, y);
14719     }
14720     else if (element == EL_SP_TERMINAL)
14721     {
14722       int xx, yy;
14723
14724       SCAN_PLAYFIELD(xx, yy)
14725       {
14726         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14727         {
14728           Bang(xx, yy);
14729         }
14730         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14731         {
14732           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14733
14734           ResetGfxAnimation(xx, yy);
14735           TEST_DrawLevelField(xx, yy);
14736         }
14737       }
14738     }
14739     else if (IS_BELT_SWITCH(element))
14740     {
14741       ToggleBeltSwitch(x, y);
14742     }
14743     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14744              element == EL_SWITCHGATE_SWITCH_DOWN ||
14745              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14746              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14747     {
14748       ToggleSwitchgateSwitch();
14749     }
14750     else if (element == EL_LIGHT_SWITCH ||
14751              element == EL_LIGHT_SWITCH_ACTIVE)
14752     {
14753       ToggleLightSwitch(x, y);
14754     }
14755     else if (element == EL_TIMEGATE_SWITCH ||
14756              element == EL_DC_TIMEGATE_SWITCH)
14757     {
14758       ActivateTimegateSwitch(x, y);
14759     }
14760     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14761              element == EL_BALLOON_SWITCH_RIGHT ||
14762              element == EL_BALLOON_SWITCH_UP    ||
14763              element == EL_BALLOON_SWITCH_DOWN  ||
14764              element == EL_BALLOON_SWITCH_NONE  ||
14765              element == EL_BALLOON_SWITCH_ANY)
14766     {
14767       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14768                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14769                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14770                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14771                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14772                              move_direction);
14773     }
14774     else if (element == EL_LAMP)
14775     {
14776       Tile[x][y] = EL_LAMP_ACTIVE;
14777       game.lights_still_needed--;
14778
14779       ResetGfxAnimation(x, y);
14780       TEST_DrawLevelField(x, y);
14781     }
14782     else if (element == EL_TIME_ORB_FULL)
14783     {
14784       Tile[x][y] = EL_TIME_ORB_EMPTY;
14785
14786       if (level.time > 0 || level.use_time_orb_bug)
14787       {
14788         TimeLeft += level.time_orb_time;
14789         game.no_level_time_limit = FALSE;
14790
14791         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14792
14793         DisplayGameControlValues();
14794       }
14795
14796       ResetGfxAnimation(x, y);
14797       TEST_DrawLevelField(x, y);
14798     }
14799     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14800              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14801     {
14802       int xx, yy;
14803
14804       game.ball_active = !game.ball_active;
14805
14806       SCAN_PLAYFIELD(xx, yy)
14807       {
14808         int e = Tile[xx][yy];
14809
14810         if (game.ball_active)
14811         {
14812           if (e == EL_EMC_MAGIC_BALL)
14813             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14814           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14815             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14816         }
14817         else
14818         {
14819           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14820             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14821           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14822             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14823         }
14824       }
14825     }
14826
14827     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14828                                         player->index_bit, dig_side);
14829
14830     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14831                                         player->index_bit, dig_side);
14832
14833     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14834                                         player->index_bit, dig_side);
14835
14836     return MP_ACTION;
14837   }
14838   else
14839   {
14840     if (!PLAYER_SWITCHING(player, x, y))
14841     {
14842       player->is_switching = TRUE;
14843       player->switch_x = x;
14844       player->switch_y = y;
14845
14846       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14847                                  player->index_bit, dig_side);
14848       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14849                                           player->index_bit, dig_side);
14850
14851       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14852                                  player->index_bit, dig_side);
14853       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14854                                           player->index_bit, dig_side);
14855     }
14856
14857     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14858                                player->index_bit, dig_side);
14859     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14860                                         player->index_bit, dig_side);
14861
14862     return MP_NO_ACTION;
14863   }
14864
14865   player->push_delay = -1;
14866
14867   if (is_player)                // function can also be called by EL_PENGUIN
14868   {
14869     if (Tile[x][y] != element)          // really digged/collected something
14870     {
14871       player->is_collecting = !player->is_digging;
14872       player->is_active = TRUE;
14873
14874       player->last_removed_element = element;
14875     }
14876   }
14877
14878   return MP_MOVING;
14879 }
14880
14881 static boolean DigFieldByCE(int x, int y, int digging_element)
14882 {
14883   int element = Tile[x][y];
14884
14885   if (!IS_FREE(x, y))
14886   {
14887     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14888                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14889                   ACTION_BREAKING);
14890
14891     // no element can dig solid indestructible elements
14892     if (IS_INDESTRUCTIBLE(element) &&
14893         !IS_DIGGABLE(element) &&
14894         !IS_COLLECTIBLE(element))
14895       return FALSE;
14896
14897     if (AmoebaNr[x][y] &&
14898         (element == EL_AMOEBA_FULL ||
14899          element == EL_BD_AMOEBA ||
14900          element == EL_AMOEBA_GROWING))
14901     {
14902       AmoebaCnt[AmoebaNr[x][y]]--;
14903       AmoebaCnt2[AmoebaNr[x][y]]--;
14904     }
14905
14906     if (IS_MOVING(x, y))
14907       RemoveMovingField(x, y);
14908     else
14909     {
14910       RemoveField(x, y);
14911       TEST_DrawLevelField(x, y);
14912     }
14913
14914     // if digged element was about to explode, prevent the explosion
14915     ExplodeField[x][y] = EX_TYPE_NONE;
14916
14917     PlayLevelSoundAction(x, y, action);
14918   }
14919
14920   Store[x][y] = EL_EMPTY;
14921
14922   // this makes it possible to leave the removed element again
14923   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14924     Store[x][y] = element;
14925
14926   return TRUE;
14927 }
14928
14929 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14930 {
14931   int jx = player->jx, jy = player->jy;
14932   int x = jx + dx, y = jy + dy;
14933   int snap_direction = (dx == -1 ? MV_LEFT  :
14934                         dx == +1 ? MV_RIGHT :
14935                         dy == -1 ? MV_UP    :
14936                         dy == +1 ? MV_DOWN  : MV_NONE);
14937   boolean can_continue_snapping = (level.continuous_snapping &&
14938                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14939
14940   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14941     return FALSE;
14942
14943   if (!player->active || !IN_LEV_FIELD(x, y))
14944     return FALSE;
14945
14946   if (dx && dy)
14947     return FALSE;
14948
14949   if (!dx && !dy)
14950   {
14951     if (player->MovPos == 0)
14952       player->is_pushing = FALSE;
14953
14954     player->is_snapping = FALSE;
14955
14956     if (player->MovPos == 0)
14957     {
14958       player->is_moving = FALSE;
14959       player->is_digging = FALSE;
14960       player->is_collecting = FALSE;
14961     }
14962
14963     return FALSE;
14964   }
14965
14966   // prevent snapping with already pressed snap key when not allowed
14967   if (player->is_snapping && !can_continue_snapping)
14968     return FALSE;
14969
14970   player->MovDir = snap_direction;
14971
14972   if (player->MovPos == 0)
14973   {
14974     player->is_moving = FALSE;
14975     player->is_digging = FALSE;
14976     player->is_collecting = FALSE;
14977   }
14978
14979   player->is_dropping = FALSE;
14980   player->is_dropping_pressed = FALSE;
14981   player->drop_pressed_delay = 0;
14982
14983   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14984     return FALSE;
14985
14986   player->is_snapping = TRUE;
14987   player->is_active = TRUE;
14988
14989   if (player->MovPos == 0)
14990   {
14991     player->is_moving = FALSE;
14992     player->is_digging = FALSE;
14993     player->is_collecting = FALSE;
14994   }
14995
14996   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14997     TEST_DrawLevelField(player->last_jx, player->last_jy);
14998
14999   TEST_DrawLevelField(x, y);
15000
15001   return TRUE;
15002 }
15003
15004 static boolean DropElement(struct PlayerInfo *player)
15005 {
15006   int old_element, new_element;
15007   int dropx = player->jx, dropy = player->jy;
15008   int drop_direction = player->MovDir;
15009   int drop_side = drop_direction;
15010   int drop_element = get_next_dropped_element(player);
15011
15012   /* do not drop an element on top of another element; when holding drop key
15013      pressed without moving, dropped element must move away before the next
15014      element can be dropped (this is especially important if the next element
15015      is dynamite, which can be placed on background for historical reasons) */
15016   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
15017     return MP_ACTION;
15018
15019   if (IS_THROWABLE(drop_element))
15020   {
15021     dropx += GET_DX_FROM_DIR(drop_direction);
15022     dropy += GET_DY_FROM_DIR(drop_direction);
15023
15024     if (!IN_LEV_FIELD(dropx, dropy))
15025       return FALSE;
15026   }
15027
15028   old_element = Tile[dropx][dropy];     // old element at dropping position
15029   new_element = drop_element;           // default: no change when dropping
15030
15031   // check if player is active, not moving and ready to drop
15032   if (!player->active || player->MovPos || player->drop_delay > 0)
15033     return FALSE;
15034
15035   // check if player has anything that can be dropped
15036   if (new_element == EL_UNDEFINED)
15037     return FALSE;
15038
15039   // only set if player has anything that can be dropped
15040   player->is_dropping_pressed = TRUE;
15041
15042   // check if drop key was pressed long enough for EM style dynamite
15043   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15044     return FALSE;
15045
15046   // check if anything can be dropped at the current position
15047   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15048     return FALSE;
15049
15050   // collected custom elements can only be dropped on empty fields
15051   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15052     return FALSE;
15053
15054   if (old_element != EL_EMPTY)
15055     Back[dropx][dropy] = old_element;   // store old element on this field
15056
15057   ResetGfxAnimation(dropx, dropy);
15058   ResetRandomAnimationValue(dropx, dropy);
15059
15060   if (player->inventory_size > 0 ||
15061       player->inventory_infinite_element != EL_UNDEFINED)
15062   {
15063     if (player->inventory_size > 0)
15064     {
15065       player->inventory_size--;
15066
15067       DrawGameDoorValues();
15068
15069       if (new_element == EL_DYNAMITE)
15070         new_element = EL_DYNAMITE_ACTIVE;
15071       else if (new_element == EL_EM_DYNAMITE)
15072         new_element = EL_EM_DYNAMITE_ACTIVE;
15073       else if (new_element == EL_SP_DISK_RED)
15074         new_element = EL_SP_DISK_RED_ACTIVE;
15075     }
15076
15077     Tile[dropx][dropy] = new_element;
15078
15079     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15080       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15081                           el2img(Tile[dropx][dropy]), 0);
15082
15083     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15084
15085     // needed if previous element just changed to "empty" in the last frame
15086     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15087
15088     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15089                                player->index_bit, drop_side);
15090     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15091                                         CE_PLAYER_DROPS_X,
15092                                         player->index_bit, drop_side);
15093
15094     TestIfElementTouchesCustomElement(dropx, dropy);
15095   }
15096   else          // player is dropping a dyna bomb
15097   {
15098     player->dynabombs_left--;
15099
15100     Tile[dropx][dropy] = new_element;
15101
15102     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15103       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15104                           el2img(Tile[dropx][dropy]), 0);
15105
15106     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15107   }
15108
15109   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15110     InitField_WithBug1(dropx, dropy, FALSE);
15111
15112   new_element = Tile[dropx][dropy];     // element might have changed
15113
15114   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15115       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15116   {
15117     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15118       MovDir[dropx][dropy] = drop_direction;
15119
15120     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15121
15122     // do not cause impact style collision by dropping elements that can fall
15123     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15124   }
15125
15126   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15127   player->is_dropping = TRUE;
15128
15129   player->drop_pressed_delay = 0;
15130   player->is_dropping_pressed = FALSE;
15131
15132   player->drop_x = dropx;
15133   player->drop_y = dropy;
15134
15135   return TRUE;
15136 }
15137
15138 // ----------------------------------------------------------------------------
15139 // game sound playing functions
15140 // ----------------------------------------------------------------------------
15141
15142 static int *loop_sound_frame = NULL;
15143 static int *loop_sound_volume = NULL;
15144
15145 void InitPlayLevelSound(void)
15146 {
15147   int num_sounds = getSoundListSize();
15148
15149   checked_free(loop_sound_frame);
15150   checked_free(loop_sound_volume);
15151
15152   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15153   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15154 }
15155
15156 static void PlayLevelSound(int x, int y, int nr)
15157 {
15158   int sx = SCREENX(x), sy = SCREENY(y);
15159   int volume, stereo_position;
15160   int max_distance = 8;
15161   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15162
15163   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15164       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15165     return;
15166
15167   if (!IN_LEV_FIELD(x, y) ||
15168       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15169       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15170     return;
15171
15172   volume = SOUND_MAX_VOLUME;
15173
15174   if (!IN_SCR_FIELD(sx, sy))
15175   {
15176     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15177     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15178
15179     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15180   }
15181
15182   stereo_position = (SOUND_MAX_LEFT +
15183                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15184                      (SCR_FIELDX + 2 * max_distance));
15185
15186   if (IS_LOOP_SOUND(nr))
15187   {
15188     /* This assures that quieter loop sounds do not overwrite louder ones,
15189        while restarting sound volume comparison with each new game frame. */
15190
15191     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15192       return;
15193
15194     loop_sound_volume[nr] = volume;
15195     loop_sound_frame[nr] = FrameCounter;
15196   }
15197
15198   PlaySoundExt(nr, volume, stereo_position, type);
15199 }
15200
15201 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15202 {
15203   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15204                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15205                  y < LEVELY(BY1) ? LEVELY(BY1) :
15206                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15207                  sound_action);
15208 }
15209
15210 static void PlayLevelSoundAction(int x, int y, int action)
15211 {
15212   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15213 }
15214
15215 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15216 {
15217   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15218
15219   if (sound_effect != SND_UNDEFINED)
15220     PlayLevelSound(x, y, sound_effect);
15221 }
15222
15223 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15224                                               int action)
15225 {
15226   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15227
15228   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15229     PlayLevelSound(x, y, sound_effect);
15230 }
15231
15232 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15233 {
15234   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15235
15236   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15237     PlayLevelSound(x, y, sound_effect);
15238 }
15239
15240 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15241 {
15242   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15243
15244   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15245     StopSound(sound_effect);
15246 }
15247
15248 static int getLevelMusicNr(void)
15249 {
15250   int level_pos = level_nr - leveldir_current->first_level;
15251
15252   if (levelset.music[level_nr] != MUS_UNDEFINED)
15253     return levelset.music[level_nr];            // from config file
15254   else
15255     return MAP_NOCONF_MUSIC(level_pos);         // from music dir
15256 }
15257
15258 static void FadeLevelSounds(void)
15259 {
15260   FadeSounds();
15261 }
15262
15263 static void FadeLevelMusic(void)
15264 {
15265   int music_nr = getLevelMusicNr();
15266   char *curr_music = getCurrentlyPlayingMusicFilename();
15267   char *next_music = getMusicInfoEntryFilename(music_nr);
15268
15269   if (!strEqual(curr_music, next_music))
15270     FadeMusic();
15271 }
15272
15273 void FadeLevelSoundsAndMusic(void)
15274 {
15275   FadeLevelSounds();
15276   FadeLevelMusic();
15277 }
15278
15279 static void PlayLevelMusic(void)
15280 {
15281   int music_nr = getLevelMusicNr();
15282   char *curr_music = getCurrentlyPlayingMusicFilename();
15283   char *next_music = getMusicInfoEntryFilename(music_nr);
15284
15285   if (!strEqual(curr_music, next_music))
15286     PlayMusicLoop(music_nr);
15287 }
15288
15289 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15290 {
15291   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15292   int offset = 0;
15293   int x = xx - offset;
15294   int y = yy - offset;
15295
15296   switch (sample)
15297   {
15298     case SOUND_blank:
15299       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15300       break;
15301
15302     case SOUND_roll:
15303       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15304       break;
15305
15306     case SOUND_stone:
15307       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15308       break;
15309
15310     case SOUND_nut:
15311       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15312       break;
15313
15314     case SOUND_crack:
15315       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15316       break;
15317
15318     case SOUND_bug:
15319       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15320       break;
15321
15322     case SOUND_tank:
15323       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15324       break;
15325
15326     case SOUND_android_clone:
15327       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15328       break;
15329
15330     case SOUND_android_move:
15331       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15332       break;
15333
15334     case SOUND_spring:
15335       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15336       break;
15337
15338     case SOUND_slurp:
15339       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15340       break;
15341
15342     case SOUND_eater:
15343       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15344       break;
15345
15346     case SOUND_eater_eat:
15347       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15348       break;
15349
15350     case SOUND_alien:
15351       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15352       break;
15353
15354     case SOUND_collect:
15355       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15356       break;
15357
15358     case SOUND_diamond:
15359       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15360       break;
15361
15362     case SOUND_squash:
15363       // !!! CHECK THIS !!!
15364 #if 1
15365       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15366 #else
15367       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15368 #endif
15369       break;
15370
15371     case SOUND_wonderfall:
15372       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15373       break;
15374
15375     case SOUND_drip:
15376       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15377       break;
15378
15379     case SOUND_push:
15380       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15381       break;
15382
15383     case SOUND_dirt:
15384       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15385       break;
15386
15387     case SOUND_acid:
15388       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15389       break;
15390
15391     case SOUND_ball:
15392       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15393       break;
15394
15395     case SOUND_slide:
15396       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15397       break;
15398
15399     case SOUND_wonder:
15400       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15401       break;
15402
15403     case SOUND_door:
15404       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15405       break;
15406
15407     case SOUND_exit_open:
15408       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15409       break;
15410
15411     case SOUND_exit_leave:
15412       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15413       break;
15414
15415     case SOUND_dynamite:
15416       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15417       break;
15418
15419     case SOUND_tick:
15420       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15421       break;
15422
15423     case SOUND_press:
15424       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15425       break;
15426
15427     case SOUND_wheel:
15428       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15429       break;
15430
15431     case SOUND_boom:
15432       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15433       break;
15434
15435     case SOUND_die:
15436       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15437       break;
15438
15439     case SOUND_time:
15440       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15441       break;
15442
15443     default:
15444       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15445       break;
15446   }
15447 }
15448
15449 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15450 {
15451   int element = map_element_SP_to_RND(element_sp);
15452   int action = map_action_SP_to_RND(action_sp);
15453   int offset = (setup.sp_show_border_elements ? 0 : 1);
15454   int x = xx - offset;
15455   int y = yy - offset;
15456
15457   PlayLevelSoundElementAction(x, y, element, action);
15458 }
15459
15460 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15461 {
15462   int element = map_element_MM_to_RND(element_mm);
15463   int action = map_action_MM_to_RND(action_mm);
15464   int offset = 0;
15465   int x = xx - offset;
15466   int y = yy - offset;
15467
15468   if (!IS_MM_ELEMENT(element))
15469     element = EL_MM_DEFAULT;
15470
15471   PlayLevelSoundElementAction(x, y, element, action);
15472 }
15473
15474 void PlaySound_MM(int sound_mm)
15475 {
15476   int sound = map_sound_MM_to_RND(sound_mm);
15477
15478   if (sound == SND_UNDEFINED)
15479     return;
15480
15481   PlaySound(sound);
15482 }
15483
15484 void PlaySoundLoop_MM(int sound_mm)
15485 {
15486   int sound = map_sound_MM_to_RND(sound_mm);
15487
15488   if (sound == SND_UNDEFINED)
15489     return;
15490
15491   PlaySoundLoop(sound);
15492 }
15493
15494 void StopSound_MM(int sound_mm)
15495 {
15496   int sound = map_sound_MM_to_RND(sound_mm);
15497
15498   if (sound == SND_UNDEFINED)
15499     return;
15500
15501   StopSound(sound);
15502 }
15503
15504 void RaiseScore(int value)
15505 {
15506   game.score += value;
15507
15508   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15509
15510   DisplayGameControlValues();
15511 }
15512
15513 void RaiseScoreElement(int element)
15514 {
15515   switch (element)
15516   {
15517     case EL_EMERALD:
15518     case EL_BD_DIAMOND:
15519     case EL_EMERALD_YELLOW:
15520     case EL_EMERALD_RED:
15521     case EL_EMERALD_PURPLE:
15522     case EL_SP_INFOTRON:
15523       RaiseScore(level.score[SC_EMERALD]);
15524       break;
15525     case EL_DIAMOND:
15526       RaiseScore(level.score[SC_DIAMOND]);
15527       break;
15528     case EL_CRYSTAL:
15529       RaiseScore(level.score[SC_CRYSTAL]);
15530       break;
15531     case EL_PEARL:
15532       RaiseScore(level.score[SC_PEARL]);
15533       break;
15534     case EL_BUG:
15535     case EL_BD_BUTTERFLY:
15536     case EL_SP_ELECTRON:
15537       RaiseScore(level.score[SC_BUG]);
15538       break;
15539     case EL_SPACESHIP:
15540     case EL_BD_FIREFLY:
15541     case EL_SP_SNIKSNAK:
15542       RaiseScore(level.score[SC_SPACESHIP]);
15543       break;
15544     case EL_YAMYAM:
15545     case EL_DARK_YAMYAM:
15546       RaiseScore(level.score[SC_YAMYAM]);
15547       break;
15548     case EL_ROBOT:
15549       RaiseScore(level.score[SC_ROBOT]);
15550       break;
15551     case EL_PACMAN:
15552       RaiseScore(level.score[SC_PACMAN]);
15553       break;
15554     case EL_NUT:
15555       RaiseScore(level.score[SC_NUT]);
15556       break;
15557     case EL_DYNAMITE:
15558     case EL_EM_DYNAMITE:
15559     case EL_SP_DISK_RED:
15560     case EL_DYNABOMB_INCREASE_NUMBER:
15561     case EL_DYNABOMB_INCREASE_SIZE:
15562     case EL_DYNABOMB_INCREASE_POWER:
15563       RaiseScore(level.score[SC_DYNAMITE]);
15564       break;
15565     case EL_SHIELD_NORMAL:
15566     case EL_SHIELD_DEADLY:
15567       RaiseScore(level.score[SC_SHIELD]);
15568       break;
15569     case EL_EXTRA_TIME:
15570       RaiseScore(level.extra_time_score);
15571       break;
15572     case EL_KEY_1:
15573     case EL_KEY_2:
15574     case EL_KEY_3:
15575     case EL_KEY_4:
15576     case EL_EM_KEY_1:
15577     case EL_EM_KEY_2:
15578     case EL_EM_KEY_3:
15579     case EL_EM_KEY_4:
15580     case EL_EMC_KEY_5:
15581     case EL_EMC_KEY_6:
15582     case EL_EMC_KEY_7:
15583     case EL_EMC_KEY_8:
15584     case EL_DC_KEY_WHITE:
15585       RaiseScore(level.score[SC_KEY]);
15586       break;
15587     default:
15588       RaiseScore(element_info[element].collect_score);
15589       break;
15590   }
15591 }
15592
15593 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15594 {
15595   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15596   {
15597     if (!quick_quit)
15598     {
15599       // prevent short reactivation of overlay buttons while closing door
15600       SetOverlayActive(FALSE);
15601       UnmapGameButtons();
15602
15603       // door may still be open due to skipped or envelope style request
15604       CloseDoor(score_info_tape_play ? DOOR_CLOSE_ALL : DOOR_CLOSE_1);
15605     }
15606
15607     if (network.enabled)
15608       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15609     else
15610     {
15611       if (quick_quit)
15612         FadeSkipNextFadeIn();
15613
15614       SetGameStatus(GAME_MODE_MAIN);
15615
15616       DrawMainMenu();
15617     }
15618   }
15619   else          // continue playing the game
15620   {
15621     if (tape.playing && tape.deactivate_display)
15622       TapeDeactivateDisplayOff(TRUE);
15623
15624     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15625
15626     if (tape.playing && tape.deactivate_display)
15627       TapeDeactivateDisplayOn();
15628   }
15629 }
15630
15631 void RequestQuitGame(boolean escape_key_pressed)
15632 {
15633   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15634   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15635                         level_editor_test_game);
15636   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15637                           quick_quit || score_info_tape_play);
15638
15639   RequestQuitGameExt(skip_request, quick_quit,
15640                      "Do you really want to quit the game?");
15641 }
15642
15643 static char *getRestartGameMessage(void)
15644 {
15645   boolean play_again = hasStartedNetworkGame();
15646   static char message[MAX_OUTPUT_LINESIZE];
15647   char *game_over_text = "Game over!";
15648   char *play_again_text = " Play it again?";
15649
15650   if (level.game_engine_type == GAME_ENGINE_TYPE_MM &&
15651       game_mm.game_over_message != NULL)
15652     game_over_text = game_mm.game_over_message;
15653
15654   snprintf(message, MAX_OUTPUT_LINESIZE, "%s%s", game_over_text,
15655            (play_again ? play_again_text : ""));
15656
15657   return message;
15658 }
15659
15660 static void RequestRestartGame(void)
15661 {
15662   char *message = getRestartGameMessage();
15663   boolean has_started_game = hasStartedNetworkGame();
15664   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15665   int door_state = DOOR_CLOSE_1;
15666
15667   if (Request(message, request_mode | REQ_STAY_OPEN) && has_started_game)
15668   {
15669     CloseDoor(door_state);
15670
15671     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15672   }
15673   else
15674   {
15675     // if game was invoked from level editor, also close tape recorder door
15676     if (level_editor_test_game)
15677       door_state = DOOR_CLOSE_ALL;
15678
15679     CloseDoor(door_state);
15680
15681     SetGameStatus(GAME_MODE_MAIN);
15682
15683     DrawMainMenu();
15684   }
15685 }
15686
15687 boolean CheckRestartGame(void)
15688 {
15689   static int game_over_delay = 0;
15690   int game_over_delay_value = 50;
15691   boolean game_over = checkGameFailed();
15692
15693   if (!game_over)
15694   {
15695     game_over_delay = game_over_delay_value;
15696
15697     return FALSE;
15698   }
15699
15700   if (game_over_delay > 0)
15701   {
15702     if (game_over_delay == game_over_delay_value / 2)
15703       PlaySound(SND_GAME_LOSING);
15704
15705     game_over_delay--;
15706
15707     return FALSE;
15708   }
15709
15710   // do not handle game over if request dialog is already active
15711   if (game.request_active)
15712     return FALSE;
15713
15714   // do not ask to play again if game was never actually played
15715   if (!game.GamePlayed)
15716     return FALSE;
15717
15718   // do not ask to play again if this was disabled in setup menu
15719   if (!setup.ask_on_game_over)
15720     return FALSE;
15721
15722   RequestRestartGame();
15723
15724   return TRUE;
15725 }
15726
15727 boolean checkGameSolved(void)
15728 {
15729   // set for all game engines if level was solved
15730   return game.LevelSolved_GameEnd;
15731 }
15732
15733 boolean checkGameFailed(void)
15734 {
15735   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15736     return (game_em.game_over && !game_em.level_solved);
15737   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15738     return (game_sp.game_over && !game_sp.level_solved);
15739   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15740     return (game_mm.game_over && !game_mm.level_solved);
15741   else                          // GAME_ENGINE_TYPE_RND
15742     return (game.GameOver && !game.LevelSolved);
15743 }
15744
15745 boolean checkGameEnded(void)
15746 {
15747   return (checkGameSolved() || checkGameFailed());
15748 }
15749
15750
15751 // ----------------------------------------------------------------------------
15752 // random generator functions
15753 // ----------------------------------------------------------------------------
15754
15755 unsigned int InitEngineRandom_RND(int seed)
15756 {
15757   game.num_random_calls = 0;
15758
15759   return InitEngineRandom(seed);
15760 }
15761
15762 unsigned int RND(int max)
15763 {
15764   if (max > 0)
15765   {
15766     game.num_random_calls++;
15767
15768     return GetEngineRandom(max);
15769   }
15770
15771   return 0;
15772 }
15773
15774
15775 // ----------------------------------------------------------------------------
15776 // game engine snapshot handling functions
15777 // ----------------------------------------------------------------------------
15778
15779 struct EngineSnapshotInfo
15780 {
15781   // runtime values for custom element collect score
15782   int collect_score[NUM_CUSTOM_ELEMENTS];
15783
15784   // runtime values for group element choice position
15785   int choice_pos[NUM_GROUP_ELEMENTS];
15786
15787   // runtime values for belt position animations
15788   int belt_graphic[4][NUM_BELT_PARTS];
15789   int belt_anim_mode[4][NUM_BELT_PARTS];
15790 };
15791
15792 static struct EngineSnapshotInfo engine_snapshot_rnd;
15793 static char *snapshot_level_identifier = NULL;
15794 static int snapshot_level_nr = -1;
15795
15796 static void SaveEngineSnapshotValues_RND(void)
15797 {
15798   static int belt_base_active_element[4] =
15799   {
15800     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15801     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15802     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15803     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15804   };
15805   int i, j;
15806
15807   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15808   {
15809     int element = EL_CUSTOM_START + i;
15810
15811     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15812   }
15813
15814   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15815   {
15816     int element = EL_GROUP_START + i;
15817
15818     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15819   }
15820
15821   for (i = 0; i < 4; i++)
15822   {
15823     for (j = 0; j < NUM_BELT_PARTS; j++)
15824     {
15825       int element = belt_base_active_element[i] + j;
15826       int graphic = el2img(element);
15827       int anim_mode = graphic_info[graphic].anim_mode;
15828
15829       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15830       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15831     }
15832   }
15833 }
15834
15835 static void LoadEngineSnapshotValues_RND(void)
15836 {
15837   unsigned int num_random_calls = game.num_random_calls;
15838   int i, j;
15839
15840   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15841   {
15842     int element = EL_CUSTOM_START + i;
15843
15844     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15845   }
15846
15847   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15848   {
15849     int element = EL_GROUP_START + i;
15850
15851     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15852   }
15853
15854   for (i = 0; i < 4; i++)
15855   {
15856     for (j = 0; j < NUM_BELT_PARTS; j++)
15857     {
15858       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15859       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15860
15861       graphic_info[graphic].anim_mode = anim_mode;
15862     }
15863   }
15864
15865   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15866   {
15867     InitRND(tape.random_seed);
15868     for (i = 0; i < num_random_calls; i++)
15869       RND(1);
15870   }
15871
15872   if (game.num_random_calls != num_random_calls)
15873   {
15874     Error("number of random calls out of sync");
15875     Error("number of random calls should be %d", num_random_calls);
15876     Error("number of random calls is %d", game.num_random_calls);
15877
15878     Fail("this should not happen -- please debug");
15879   }
15880 }
15881
15882 void FreeEngineSnapshotSingle(void)
15883 {
15884   FreeSnapshotSingle();
15885
15886   setString(&snapshot_level_identifier, NULL);
15887   snapshot_level_nr = -1;
15888 }
15889
15890 void FreeEngineSnapshotList(void)
15891 {
15892   FreeSnapshotList();
15893 }
15894
15895 static ListNode *SaveEngineSnapshotBuffers(void)
15896 {
15897   ListNode *buffers = NULL;
15898
15899   // copy some special values to a structure better suited for the snapshot
15900
15901   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15902     SaveEngineSnapshotValues_RND();
15903   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15904     SaveEngineSnapshotValues_EM();
15905   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15906     SaveEngineSnapshotValues_SP(&buffers);
15907   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15908     SaveEngineSnapshotValues_MM();
15909
15910   // save values stored in special snapshot structure
15911
15912   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15913     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15914   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15915     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15916   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15917     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15918   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15919     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15920
15921   // save further RND engine values
15922
15923   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15924   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15925   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15926
15927   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15928   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15929   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15930   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15931   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15932
15933   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15934   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15935   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15936
15937   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15938
15939   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15940   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15941
15942   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15943   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15944   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15945   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15946   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15947   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15948   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15949   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15950   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15951   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15952   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15953   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15954   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15955   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15956   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15957   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15958   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15959   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15960
15961   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15962   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15963
15964   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15965   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15966   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15967
15968   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15969   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15970
15971   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15972   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15973   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
15974   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15975   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15976   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15977
15978   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15979   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15980
15981 #if 0
15982   ListNode *node = engine_snapshot_list_rnd;
15983   int num_bytes = 0;
15984
15985   while (node != NULL)
15986   {
15987     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15988
15989     node = node->next;
15990   }
15991
15992   Debug("game:playing:SaveEngineSnapshotBuffers",
15993         "size of engine snapshot: %d bytes", num_bytes);
15994 #endif
15995
15996   return buffers;
15997 }
15998
15999 void SaveEngineSnapshotSingle(void)
16000 {
16001   ListNode *buffers = SaveEngineSnapshotBuffers();
16002
16003   // finally save all snapshot buffers to single snapshot
16004   SaveSnapshotSingle(buffers);
16005
16006   // save level identification information
16007   setString(&snapshot_level_identifier, leveldir_current->identifier);
16008   snapshot_level_nr = level_nr;
16009 }
16010
16011 boolean CheckSaveEngineSnapshotToList(void)
16012 {
16013   boolean save_snapshot =
16014     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
16015      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
16016       game.snapshot.changed_action) ||
16017      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16018       game.snapshot.collected_item));
16019
16020   game.snapshot.changed_action = FALSE;
16021   game.snapshot.collected_item = FALSE;
16022   game.snapshot.save_snapshot = save_snapshot;
16023
16024   return save_snapshot;
16025 }
16026
16027 void SaveEngineSnapshotToList(void)
16028 {
16029   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
16030       tape.quick_resume)
16031     return;
16032
16033   ListNode *buffers = SaveEngineSnapshotBuffers();
16034
16035   // finally save all snapshot buffers to snapshot list
16036   SaveSnapshotToList(buffers);
16037 }
16038
16039 void SaveEngineSnapshotToListInitial(void)
16040 {
16041   FreeEngineSnapshotList();
16042
16043   SaveEngineSnapshotToList();
16044 }
16045
16046 static void LoadEngineSnapshotValues(void)
16047 {
16048   // restore special values from snapshot structure
16049
16050   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16051     LoadEngineSnapshotValues_RND();
16052   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16053     LoadEngineSnapshotValues_EM();
16054   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16055     LoadEngineSnapshotValues_SP();
16056   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16057     LoadEngineSnapshotValues_MM();
16058 }
16059
16060 void LoadEngineSnapshotSingle(void)
16061 {
16062   LoadSnapshotSingle();
16063
16064   LoadEngineSnapshotValues();
16065 }
16066
16067 static void LoadEngineSnapshot_Undo(int steps)
16068 {
16069   LoadSnapshotFromList_Older(steps);
16070
16071   LoadEngineSnapshotValues();
16072 }
16073
16074 static void LoadEngineSnapshot_Redo(int steps)
16075 {
16076   LoadSnapshotFromList_Newer(steps);
16077
16078   LoadEngineSnapshotValues();
16079 }
16080
16081 boolean CheckEngineSnapshotSingle(void)
16082 {
16083   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16084           snapshot_level_nr == level_nr);
16085 }
16086
16087 boolean CheckEngineSnapshotList(void)
16088 {
16089   return CheckSnapshotList();
16090 }
16091
16092
16093 // ---------- new game button stuff -------------------------------------------
16094
16095 static struct
16096 {
16097   int graphic;
16098   struct XY *pos;
16099   int gadget_id;
16100   boolean *setup_value;
16101   boolean allowed_on_tape;
16102   boolean is_touch_button;
16103   char *infotext;
16104 } gamebutton_info[NUM_GAME_BUTTONS] =
16105 {
16106   {
16107     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16108     GAME_CTRL_ID_STOP,                          NULL,
16109     TRUE, FALSE,                                "stop game"
16110   },
16111   {
16112     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16113     GAME_CTRL_ID_PAUSE,                         NULL,
16114     TRUE, FALSE,                                "pause game"
16115   },
16116   {
16117     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16118     GAME_CTRL_ID_PLAY,                          NULL,
16119     TRUE, FALSE,                                "play game"
16120   },
16121   {
16122     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16123     GAME_CTRL_ID_UNDO,                          NULL,
16124     TRUE, FALSE,                                "undo step"
16125   },
16126   {
16127     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16128     GAME_CTRL_ID_REDO,                          NULL,
16129     TRUE, FALSE,                                "redo step"
16130   },
16131   {
16132     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16133     GAME_CTRL_ID_SAVE,                          NULL,
16134     TRUE, FALSE,                                "save game"
16135   },
16136   {
16137     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16138     GAME_CTRL_ID_PAUSE2,                        NULL,
16139     TRUE, FALSE,                                "pause game"
16140   },
16141   {
16142     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16143     GAME_CTRL_ID_LOAD,                          NULL,
16144     TRUE, FALSE,                                "load game"
16145   },
16146   {
16147     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16148     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16149     FALSE, FALSE,                               "stop game"
16150   },
16151   {
16152     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16153     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16154     FALSE, FALSE,                               "pause game"
16155   },
16156   {
16157     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16158     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16159     FALSE, FALSE,                               "play game"
16160   },
16161   {
16162     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16163     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16164     FALSE, TRUE,                                "stop game"
16165   },
16166   {
16167     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16168     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16169     FALSE, TRUE,                                "pause game"
16170   },
16171   {
16172     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16173     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16174     TRUE, FALSE,                                "background music on/off"
16175   },
16176   {
16177     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16178     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16179     TRUE, FALSE,                                "sound loops on/off"
16180   },
16181   {
16182     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16183     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16184     TRUE, FALSE,                                "normal sounds on/off"
16185   },
16186   {
16187     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16188     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16189     FALSE, FALSE,                               "background music on/off"
16190   },
16191   {
16192     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16193     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16194     FALSE, FALSE,                               "sound loops on/off"
16195   },
16196   {
16197     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16198     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16199     FALSE, FALSE,                               "normal sounds on/off"
16200   }
16201 };
16202
16203 void CreateGameButtons(void)
16204 {
16205   int i;
16206
16207   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16208   {
16209     int graphic = gamebutton_info[i].graphic;
16210     struct GraphicInfo *gfx = &graphic_info[graphic];
16211     struct XY *pos = gamebutton_info[i].pos;
16212     struct GadgetInfo *gi;
16213     int button_type;
16214     boolean checked;
16215     unsigned int event_mask;
16216     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16217     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16218     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16219     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16220     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16221     int gd_x   = gfx->src_x;
16222     int gd_y   = gfx->src_y;
16223     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16224     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16225     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16226     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16227     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16228     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16229     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16230     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16231     int id = i;
16232
16233     // do not use touch buttons if overlay touch buttons are disabled
16234     if (is_touch_button && !setup.touch.overlay_buttons)
16235       continue;
16236
16237     if (gfx->bitmap == NULL)
16238     {
16239       game_gadget[id] = NULL;
16240
16241       continue;
16242     }
16243
16244     if (id == GAME_CTRL_ID_STOP ||
16245         id == GAME_CTRL_ID_PANEL_STOP ||
16246         id == GAME_CTRL_ID_TOUCH_STOP ||
16247         id == GAME_CTRL_ID_PLAY ||
16248         id == GAME_CTRL_ID_PANEL_PLAY ||
16249         id == GAME_CTRL_ID_SAVE ||
16250         id == GAME_CTRL_ID_LOAD)
16251     {
16252       button_type = GD_TYPE_NORMAL_BUTTON;
16253       checked = FALSE;
16254       event_mask = GD_EVENT_RELEASED;
16255     }
16256     else if (id == GAME_CTRL_ID_UNDO ||
16257              id == GAME_CTRL_ID_REDO)
16258     {
16259       button_type = GD_TYPE_NORMAL_BUTTON;
16260       checked = FALSE;
16261       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16262     }
16263     else
16264     {
16265       button_type = GD_TYPE_CHECK_BUTTON;
16266       checked = (gamebutton_info[i].setup_value != NULL ?
16267                  *gamebutton_info[i].setup_value : FALSE);
16268       event_mask = GD_EVENT_PRESSED;
16269     }
16270
16271     gi = CreateGadget(GDI_CUSTOM_ID, id,
16272                       GDI_IMAGE_ID, graphic,
16273                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16274                       GDI_X, base_x + x,
16275                       GDI_Y, base_y + y,
16276                       GDI_WIDTH, gfx->width,
16277                       GDI_HEIGHT, gfx->height,
16278                       GDI_TYPE, button_type,
16279                       GDI_STATE, GD_BUTTON_UNPRESSED,
16280                       GDI_CHECKED, checked,
16281                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16282                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16283                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16284                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16285                       GDI_DIRECT_DRAW, FALSE,
16286                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16287                       GDI_EVENT_MASK, event_mask,
16288                       GDI_CALLBACK_ACTION, HandleGameButtons,
16289                       GDI_END);
16290
16291     if (gi == NULL)
16292       Fail("cannot create gadget");
16293
16294     game_gadget[id] = gi;
16295   }
16296 }
16297
16298 void FreeGameButtons(void)
16299 {
16300   int i;
16301
16302   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16303     FreeGadget(game_gadget[i]);
16304 }
16305
16306 static void UnmapGameButtonsAtSamePosition(int id)
16307 {
16308   int i;
16309
16310   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16311     if (i != id &&
16312         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16313         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16314       UnmapGadget(game_gadget[i]);
16315 }
16316
16317 static void UnmapGameButtonsAtSamePosition_All(void)
16318 {
16319   if (setup.show_load_save_buttons)
16320   {
16321     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16322     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16323     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16324   }
16325   else if (setup.show_undo_redo_buttons)
16326   {
16327     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16328     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16329     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16330   }
16331   else
16332   {
16333     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16334     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16335     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16336
16337     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16338     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16339     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16340   }
16341 }
16342
16343 void MapLoadSaveButtons(void)
16344 {
16345   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16346   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16347
16348   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
16349   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
16350 }
16351
16352 void MapUndoRedoButtons(void)
16353 {
16354   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16355   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16356
16357   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16358   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16359 }
16360
16361 void ModifyPauseButtons(void)
16362 {
16363   static int ids[] =
16364   {
16365     GAME_CTRL_ID_PAUSE,
16366     GAME_CTRL_ID_PAUSE2,
16367     GAME_CTRL_ID_PANEL_PAUSE,
16368     GAME_CTRL_ID_TOUCH_PAUSE,
16369     -1
16370   };
16371   int i;
16372
16373   for (i = 0; ids[i] > -1; i++)
16374     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16375 }
16376
16377 static void MapGameButtonsExt(boolean on_tape)
16378 {
16379   int i;
16380
16381   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16382   {
16383     if ((i == GAME_CTRL_ID_UNDO ||
16384          i == GAME_CTRL_ID_REDO) &&
16385         game_status != GAME_MODE_PLAYING)
16386       continue;
16387
16388     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16389       MapGadget(game_gadget[i]);
16390   }
16391
16392   UnmapGameButtonsAtSamePosition_All();
16393
16394   RedrawGameButtons();
16395 }
16396
16397 static void UnmapGameButtonsExt(boolean on_tape)
16398 {
16399   int i;
16400
16401   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16402     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16403       UnmapGadget(game_gadget[i]);
16404 }
16405
16406 static void RedrawGameButtonsExt(boolean on_tape)
16407 {
16408   int i;
16409
16410   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16411     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16412       RedrawGadget(game_gadget[i]);
16413 }
16414
16415 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16416 {
16417   if (gi == NULL)
16418     return;
16419
16420   gi->checked = state;
16421 }
16422
16423 static void RedrawSoundButtonGadget(int id)
16424 {
16425   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16426              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16427              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16428              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16429              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16430              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16431              id);
16432
16433   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16434   RedrawGadget(game_gadget[id2]);
16435 }
16436
16437 void MapGameButtons(void)
16438 {
16439   MapGameButtonsExt(FALSE);
16440 }
16441
16442 void UnmapGameButtons(void)
16443 {
16444   UnmapGameButtonsExt(FALSE);
16445 }
16446
16447 void RedrawGameButtons(void)
16448 {
16449   RedrawGameButtonsExt(FALSE);
16450 }
16451
16452 void MapGameButtonsOnTape(void)
16453 {
16454   MapGameButtonsExt(TRUE);
16455 }
16456
16457 void UnmapGameButtonsOnTape(void)
16458 {
16459   UnmapGameButtonsExt(TRUE);
16460 }
16461
16462 void RedrawGameButtonsOnTape(void)
16463 {
16464   RedrawGameButtonsExt(TRUE);
16465 }
16466
16467 static void GameUndoRedoExt(void)
16468 {
16469   ClearPlayerAction();
16470
16471   tape.pausing = TRUE;
16472
16473   RedrawPlayfield();
16474   UpdateAndDisplayGameControlValues();
16475
16476   DrawCompleteVideoDisplay();
16477   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16478   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16479   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16480
16481   ModifyPauseButtons();
16482
16483   BackToFront();
16484 }
16485
16486 static void GameUndo(int steps)
16487 {
16488   if (!CheckEngineSnapshotList())
16489     return;
16490
16491   int tape_property_bits = tape.property_bits;
16492
16493   LoadEngineSnapshot_Undo(steps);
16494
16495   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16496
16497   GameUndoRedoExt();
16498 }
16499
16500 static void GameRedo(int steps)
16501 {
16502   if (!CheckEngineSnapshotList())
16503     return;
16504
16505   int tape_property_bits = tape.property_bits;
16506
16507   LoadEngineSnapshot_Redo(steps);
16508
16509   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16510
16511   GameUndoRedoExt();
16512 }
16513
16514 static void HandleGameButtonsExt(int id, int button)
16515 {
16516   static boolean game_undo_executed = FALSE;
16517   int steps = BUTTON_STEPSIZE(button);
16518   boolean handle_game_buttons =
16519     (game_status == GAME_MODE_PLAYING ||
16520      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16521
16522   if (!handle_game_buttons)
16523     return;
16524
16525   switch (id)
16526   {
16527     case GAME_CTRL_ID_STOP:
16528     case GAME_CTRL_ID_PANEL_STOP:
16529     case GAME_CTRL_ID_TOUCH_STOP:
16530       TapeStopGame();
16531
16532       break;
16533
16534     case GAME_CTRL_ID_PAUSE:
16535     case GAME_CTRL_ID_PAUSE2:
16536     case GAME_CTRL_ID_PANEL_PAUSE:
16537     case GAME_CTRL_ID_TOUCH_PAUSE:
16538       if (network.enabled && game_status == GAME_MODE_PLAYING)
16539       {
16540         if (tape.pausing)
16541           SendToServer_ContinuePlaying();
16542         else
16543           SendToServer_PausePlaying();
16544       }
16545       else
16546         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16547
16548       game_undo_executed = FALSE;
16549
16550       break;
16551
16552     case GAME_CTRL_ID_PLAY:
16553     case GAME_CTRL_ID_PANEL_PLAY:
16554       if (game_status == GAME_MODE_MAIN)
16555       {
16556         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16557       }
16558       else if (tape.pausing)
16559       {
16560         if (network.enabled)
16561           SendToServer_ContinuePlaying();
16562         else
16563           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16564       }
16565       break;
16566
16567     case GAME_CTRL_ID_UNDO:
16568       // Important: When using "save snapshot when collecting an item" mode,
16569       // load last (current) snapshot for first "undo" after pressing "pause"
16570       // (else the last-but-one snapshot would be loaded, because the snapshot
16571       // pointer already points to the last snapshot when pressing "pause",
16572       // which is fine for "every step/move" mode, but not for "every collect")
16573       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16574           !game_undo_executed)
16575         steps--;
16576
16577       game_undo_executed = TRUE;
16578
16579       GameUndo(steps);
16580       break;
16581
16582     case GAME_CTRL_ID_REDO:
16583       GameRedo(steps);
16584       break;
16585
16586     case GAME_CTRL_ID_SAVE:
16587       TapeQuickSave();
16588       break;
16589
16590     case GAME_CTRL_ID_LOAD:
16591       TapeQuickLoad();
16592       break;
16593
16594     case SOUND_CTRL_ID_MUSIC:
16595     case SOUND_CTRL_ID_PANEL_MUSIC:
16596       if (setup.sound_music)
16597       { 
16598         setup.sound_music = FALSE;
16599
16600         FadeMusic();
16601       }
16602       else if (audio.music_available)
16603       { 
16604         setup.sound = setup.sound_music = TRUE;
16605
16606         SetAudioMode(setup.sound);
16607
16608         if (game_status == GAME_MODE_PLAYING)
16609           PlayLevelMusic();
16610       }
16611
16612       RedrawSoundButtonGadget(id);
16613
16614       break;
16615
16616     case SOUND_CTRL_ID_LOOPS:
16617     case SOUND_CTRL_ID_PANEL_LOOPS:
16618       if (setup.sound_loops)
16619         setup.sound_loops = FALSE;
16620       else if (audio.loops_available)
16621       {
16622         setup.sound = setup.sound_loops = TRUE;
16623
16624         SetAudioMode(setup.sound);
16625       }
16626
16627       RedrawSoundButtonGadget(id);
16628
16629       break;
16630
16631     case SOUND_CTRL_ID_SIMPLE:
16632     case SOUND_CTRL_ID_PANEL_SIMPLE:
16633       if (setup.sound_simple)
16634         setup.sound_simple = FALSE;
16635       else if (audio.sound_available)
16636       {
16637         setup.sound = setup.sound_simple = TRUE;
16638
16639         SetAudioMode(setup.sound);
16640       }
16641
16642       RedrawSoundButtonGadget(id);
16643
16644       break;
16645
16646     default:
16647       break;
16648   }
16649 }
16650
16651 static void HandleGameButtons(struct GadgetInfo *gi)
16652 {
16653   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16654 }
16655
16656 void HandleSoundButtonKeys(Key key)
16657 {
16658   if (key == setup.shortcut.sound_simple)
16659     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16660   else if (key == setup.shortcut.sound_loops)
16661     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16662   else if (key == setup.shortcut.sound_music)
16663     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16664 }