fixed bug with causing explosion instead of killing player directly
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 // DEBUG SETTINGS
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 // EXPERIMENTAL STUFF
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 // EXPERIMENTAL STUFF
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 // for DigField()
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 // for MovePlayer()
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 // for ScrollPlayer()
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 // for Bang()/Explode()
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 // game panel display and control definitions
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 // values for delayed check of falling and moving elements and for collision
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 // values for initial player move delay (initial delay counter value)
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 // values for player movement speed (which is in fact a delay value)
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 // values for scroll positions
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 // values for other actions
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
883                                  RND(element_info[e].step_delay_random))
884 #define GET_MAX_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
885                                     (element_info[e].step_delay_random))
886 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
887                                  RND(element_info[e].ce_value_random_initial))
888 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
889 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
890                                  RND((c)->delay_random * (c)->delay_frames))
891 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
892                                  RND((c)->delay_random))
893
894
895 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
896          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
897
898 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
899         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
900          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
901          (be) + (e) - EL_SELF)
902
903 #define GET_PLAYER_FROM_BITS(p)                                         \
904         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
905
906 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
907         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
908          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
909          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
910          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
911          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
912          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
913          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
914          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
915          (e))
916
917 #define CAN_GROW_INTO(e)                                                \
918         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (condition)))
923
924 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
925                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
926                                         (CAN_MOVE_INTO_ACID(e) &&       \
927                                          Tile[x][y] == EL_ACID) ||      \
928                                         (condition)))
929
930 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
931                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
932                                         (CAN_MOVE_INTO_ACID(e) &&       \
933                                          Tile[x][y] == EL_ACID) ||      \
934                                         (condition)))
935
936 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
937                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
938                                         (condition) ||                  \
939                                         (CAN_MOVE_INTO_ACID(e) &&       \
940                                          Tile[x][y] == EL_ACID) ||      \
941                                         (DONT_COLLIDE_WITH(e) &&        \
942                                          IS_PLAYER(x, y) &&             \
943                                          !PLAYER_ENEMY_PROTECTED(x, y))))
944
945 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
946         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
947
948 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
949         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
950
951 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
952         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Tile[x][y] == EL_EMC_PLANT)
953
954 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
955         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Tile[x][y]) || \
956                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
957
958 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
959         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
960
961 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
962         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Tile[x][y] == EL_DIAMOND)
963
964 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
965         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_FOOD_DARK_YAMYAM(Tile[x][y]))
966
967 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
968         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Tile[x][y]))
969
970 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
971         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Tile[x][y]))
972
973 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
974         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Tile[x][y] == EL_EXIT_OPEN || \
975                                                  Tile[x][y] == EL_EM_EXIT_OPEN || \
976                                                  Tile[x][y] == EL_STEEL_EXIT_OPEN || \
977                                                  Tile[x][y] == EL_EM_STEEL_EXIT_OPEN || \
978                                                  IS_FOOD_PENGUIN(Tile[x][y])))
979 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
980         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
981
982 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
983         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
984
985 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
986         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
987
988 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
989         (IN_LEV_FIELD(x, y) && (Tile[x][y] == EL_EMC_SPRING_BUMPER ||   \
990                                 Tile[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
991
992 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
993
994 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
995                 (!IS_PLAYER(x, y) &&                                    \
996                  IS_EQUAL_OR_IN_GROUP(Tile[x][y], MOVE_ENTER_EL(e)))
997
998 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
999         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1000
1001 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1002 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1003
1004 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1005 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1006 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1007 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1008
1009 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1010
1011 // game button identifiers
1012 #define GAME_CTRL_ID_STOP               0
1013 #define GAME_CTRL_ID_PAUSE              1
1014 #define GAME_CTRL_ID_PLAY               2
1015 #define GAME_CTRL_ID_UNDO               3
1016 #define GAME_CTRL_ID_REDO               4
1017 #define GAME_CTRL_ID_SAVE               5
1018 #define GAME_CTRL_ID_PAUSE2             6
1019 #define GAME_CTRL_ID_LOAD               7
1020 #define GAME_CTRL_ID_PANEL_STOP         8
1021 #define GAME_CTRL_ID_PANEL_PAUSE        9
1022 #define GAME_CTRL_ID_PANEL_PLAY         10
1023 #define GAME_CTRL_ID_TOUCH_STOP         11
1024 #define GAME_CTRL_ID_TOUCH_PAUSE        12
1025 #define SOUND_CTRL_ID_MUSIC             13
1026 #define SOUND_CTRL_ID_LOOPS             14
1027 #define SOUND_CTRL_ID_SIMPLE            15
1028 #define SOUND_CTRL_ID_PANEL_MUSIC       16
1029 #define SOUND_CTRL_ID_PANEL_LOOPS       17
1030 #define SOUND_CTRL_ID_PANEL_SIMPLE      18
1031
1032 #define NUM_GAME_BUTTONS                19
1033
1034
1035 // forward declaration for internal use
1036
1037 static void CreateField(int, int, int);
1038
1039 static void ResetGfxAnimation(int, int);
1040
1041 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1042 static void AdvanceFrameAndPlayerCounters(int);
1043
1044 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1045 static boolean MovePlayer(struct PlayerInfo *, int, int);
1046 static void ScrollPlayer(struct PlayerInfo *, int);
1047 static void ScrollScreen(struct PlayerInfo *, int);
1048
1049 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1050 static boolean DigFieldByCE(int, int, int);
1051 static boolean SnapField(struct PlayerInfo *, int, int);
1052 static boolean DropElement(struct PlayerInfo *);
1053
1054 static void InitBeltMovement(void);
1055 static void CloseAllOpenTimegates(void);
1056 static void CheckGravityMovement(struct PlayerInfo *);
1057 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1058 static void KillPlayerUnlessEnemyProtected(int, int);
1059 static void KillPlayerUnlessExplosionProtected(int, int);
1060
1061 static void CheckNextToConditions(int, int);
1062 static void TestIfPlayerNextToCustomElement(int, int);
1063 static void TestIfPlayerTouchesCustomElement(int, int);
1064 static void TestIfElementNextToCustomElement(int, int);
1065 static void TestIfElementTouchesCustomElement(int, int);
1066 static void TestIfElementHitsCustomElement(int, int, int);
1067
1068 static void HandleElementChange(int, int, int);
1069 static void ExecuteCustomElementAction(int, int, int, int);
1070 static boolean ChangeElement(int, int, int, int);
1071
1072 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int, int, int);
1073 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1074         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
1075 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1076         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1077 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1078         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1079 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1080         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1081 #define CheckTriggeredElementChangeByMouse(x, y, e, ev, s)              \
1082         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1083
1084 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1085 #define CheckElementChange(x, y, e, te, ev)                             \
1086         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1087 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1088         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1089 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1090         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1091 #define CheckElementChangeByMouse(x, y, e, ev, s)                       \
1092         CheckElementChangeExt(x, y, e, EL_UNDEFINED, ev, CH_PLAYER_ANY, s)
1093
1094 static void PlayLevelSound(int, int, int);
1095 static void PlayLevelSoundNearest(int, int, int);
1096 static void PlayLevelSoundAction(int, int, int);
1097 static void PlayLevelSoundElementAction(int, int, int, int);
1098 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1099 static void PlayLevelSoundActionIfLoop(int, int, int);
1100 static void StopLevelSoundActionIfLoop(int, int, int);
1101 static void PlayLevelMusic(void);
1102 static void FadeLevelSoundsAndMusic(void);
1103
1104 static void HandleGameButtons(struct GadgetInfo *);
1105
1106 int AmoebaNeighbourNr(int, int);
1107 void AmoebaToDiamond(int, int);
1108 void ContinueMoving(int, int);
1109 void Bang(int, int);
1110 void InitMovDir(int, int);
1111 void InitAmoebaNr(int, int);
1112 void NewHighScore(int, boolean);
1113
1114 void TestIfGoodThingHitsBadThing(int, int, int);
1115 void TestIfBadThingHitsGoodThing(int, int, int);
1116 void TestIfPlayerTouchesBadThing(int, int);
1117 void TestIfPlayerRunsIntoBadThing(int, int, int);
1118 void TestIfBadThingTouchesPlayer(int, int);
1119 void TestIfBadThingRunsIntoPlayer(int, int, int);
1120 void TestIfFriendTouchesBadThing(int, int);
1121 void TestIfBadThingTouchesFriend(int, int);
1122 void TestIfBadThingTouchesOtherBadThing(int, int);
1123 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1124
1125 void KillPlayer(struct PlayerInfo *);
1126 void BuryPlayer(struct PlayerInfo *);
1127 void RemovePlayer(struct PlayerInfo *);
1128 void ExitPlayer(struct PlayerInfo *);
1129
1130 static int getInvisibleActiveFromInvisibleElement(int);
1131 static int getInvisibleFromInvisibleActiveElement(int);
1132
1133 static void TestFieldAfterSnapping(int, int, int, int, int);
1134
1135 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1136
1137 // for detection of endless loops, caused by custom element programming
1138 // (using maximal playfield width x 10 is just a rough approximation)
1139 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1140
1141 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1142 {                                                                       \
1143   if (recursion_loop_detected)                                          \
1144     return (rc);                                                        \
1145                                                                         \
1146   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1147   {                                                                     \
1148     recursion_loop_detected = TRUE;                                     \
1149     recursion_loop_element = (e);                                       \
1150   }                                                                     \
1151                                                                         \
1152   recursion_loop_depth++;                                               \
1153 }
1154
1155 #define RECURSION_LOOP_DETECTION_END()                                  \
1156 {                                                                       \
1157   recursion_loop_depth--;                                               \
1158 }
1159
1160 static int recursion_loop_depth;
1161 static boolean recursion_loop_detected;
1162 static boolean recursion_loop_element;
1163
1164 static int map_player_action[MAX_PLAYERS];
1165
1166
1167 // ----------------------------------------------------------------------------
1168 // definition of elements that automatically change to other elements after
1169 // a specified time, eventually calling a function when changing
1170 // ----------------------------------------------------------------------------
1171
1172 // forward declaration for changer functions
1173 static void InitBuggyBase(int, int);
1174 static void WarnBuggyBase(int, int);
1175
1176 static void InitTrap(int, int);
1177 static void ActivateTrap(int, int);
1178 static void ChangeActiveTrap(int, int);
1179
1180 static void InitRobotWheel(int, int);
1181 static void RunRobotWheel(int, int);
1182 static void StopRobotWheel(int, int);
1183
1184 static void InitTimegateWheel(int, int);
1185 static void RunTimegateWheel(int, int);
1186
1187 static void InitMagicBallDelay(int, int);
1188 static void ActivateMagicBall(int, int);
1189
1190 struct ChangingElementInfo
1191 {
1192   int element;
1193   int target_element;
1194   int change_delay;
1195   void (*pre_change_function)(int x, int y);
1196   void (*change_function)(int x, int y);
1197   void (*post_change_function)(int x, int y);
1198 };
1199
1200 static struct ChangingElementInfo change_delay_list[] =
1201 {
1202   {
1203     EL_NUT_BREAKING,
1204     EL_EMERALD,
1205     6,
1206     NULL,
1207     NULL,
1208     NULL
1209   },
1210   {
1211     EL_PEARL_BREAKING,
1212     EL_EMPTY,
1213     8,
1214     NULL,
1215     NULL,
1216     NULL
1217   },
1218   {
1219     EL_EXIT_OPENING,
1220     EL_EXIT_OPEN,
1221     29,
1222     NULL,
1223     NULL,
1224     NULL
1225   },
1226   {
1227     EL_EXIT_CLOSING,
1228     EL_EXIT_CLOSED,
1229     29,
1230     NULL,
1231     NULL,
1232     NULL
1233   },
1234   {
1235     EL_STEEL_EXIT_OPENING,
1236     EL_STEEL_EXIT_OPEN,
1237     29,
1238     NULL,
1239     NULL,
1240     NULL
1241   },
1242   {
1243     EL_STEEL_EXIT_CLOSING,
1244     EL_STEEL_EXIT_CLOSED,
1245     29,
1246     NULL,
1247     NULL,
1248     NULL
1249   },
1250   {
1251     EL_EM_EXIT_OPENING,
1252     EL_EM_EXIT_OPEN,
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258   {
1259     EL_EM_EXIT_CLOSING,
1260     EL_EMPTY,
1261     29,
1262     NULL,
1263     NULL,
1264     NULL
1265   },
1266   {
1267     EL_EM_STEEL_EXIT_OPENING,
1268     EL_EM_STEEL_EXIT_OPEN,
1269     29,
1270     NULL,
1271     NULL,
1272     NULL
1273   },
1274   {
1275     EL_EM_STEEL_EXIT_CLOSING,
1276     EL_STEELWALL,
1277     29,
1278     NULL,
1279     NULL,
1280     NULL
1281   },
1282   {
1283     EL_SP_EXIT_OPENING,
1284     EL_SP_EXIT_OPEN,
1285     29,
1286     NULL,
1287     NULL,
1288     NULL
1289   },
1290   {
1291     EL_SP_EXIT_CLOSING,
1292     EL_SP_EXIT_CLOSED,
1293     29,
1294     NULL,
1295     NULL,
1296     NULL
1297   },
1298   {
1299     EL_SWITCHGATE_OPENING,
1300     EL_SWITCHGATE_OPEN,
1301     29,
1302     NULL,
1303     NULL,
1304     NULL
1305   },
1306   {
1307     EL_SWITCHGATE_CLOSING,
1308     EL_SWITCHGATE_CLOSED,
1309     29,
1310     NULL,
1311     NULL,
1312     NULL
1313   },
1314   {
1315     EL_TIMEGATE_OPENING,
1316     EL_TIMEGATE_OPEN,
1317     29,
1318     NULL,
1319     NULL,
1320     NULL
1321   },
1322   {
1323     EL_TIMEGATE_CLOSING,
1324     EL_TIMEGATE_CLOSED,
1325     29,
1326     NULL,
1327     NULL,
1328     NULL
1329   },
1330
1331   {
1332     EL_ACID_SPLASH_LEFT,
1333     EL_EMPTY,
1334     8,
1335     NULL,
1336     NULL,
1337     NULL
1338   },
1339   {
1340     EL_ACID_SPLASH_RIGHT,
1341     EL_EMPTY,
1342     8,
1343     NULL,
1344     NULL,
1345     NULL
1346   },
1347   {
1348     EL_SP_BUGGY_BASE,
1349     EL_SP_BUGGY_BASE_ACTIVATING,
1350     0,
1351     InitBuggyBase,
1352     NULL,
1353     NULL
1354   },
1355   {
1356     EL_SP_BUGGY_BASE_ACTIVATING,
1357     EL_SP_BUGGY_BASE_ACTIVE,
1358     0,
1359     InitBuggyBase,
1360     NULL,
1361     NULL
1362   },
1363   {
1364     EL_SP_BUGGY_BASE_ACTIVE,
1365     EL_SP_BUGGY_BASE,
1366     0,
1367     InitBuggyBase,
1368     WarnBuggyBase,
1369     NULL
1370   },
1371   {
1372     EL_TRAP,
1373     EL_TRAP_ACTIVE,
1374     0,
1375     InitTrap,
1376     NULL,
1377     ActivateTrap
1378   },
1379   {
1380     EL_TRAP_ACTIVE,
1381     EL_TRAP,
1382     31,
1383     NULL,
1384     ChangeActiveTrap,
1385     NULL
1386   },
1387   {
1388     EL_ROBOT_WHEEL_ACTIVE,
1389     EL_ROBOT_WHEEL,
1390     0,
1391     InitRobotWheel,
1392     RunRobotWheel,
1393     StopRobotWheel
1394   },
1395   {
1396     EL_TIMEGATE_SWITCH_ACTIVE,
1397     EL_TIMEGATE_SWITCH,
1398     0,
1399     InitTimegateWheel,
1400     RunTimegateWheel,
1401     NULL
1402   },
1403   {
1404     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1405     EL_DC_TIMEGATE_SWITCH,
1406     0,
1407     InitTimegateWheel,
1408     RunTimegateWheel,
1409     NULL
1410   },
1411   {
1412     EL_EMC_MAGIC_BALL_ACTIVE,
1413     EL_EMC_MAGIC_BALL_ACTIVE,
1414     0,
1415     InitMagicBallDelay,
1416     NULL,
1417     ActivateMagicBall
1418   },
1419   {
1420     EL_EMC_SPRING_BUMPER_ACTIVE,
1421     EL_EMC_SPRING_BUMPER,
1422     8,
1423     NULL,
1424     NULL,
1425     NULL
1426   },
1427   {
1428     EL_DIAGONAL_SHRINKING,
1429     EL_UNDEFINED,
1430     0,
1431     NULL,
1432     NULL,
1433     NULL
1434   },
1435   {
1436     EL_DIAGONAL_GROWING,
1437     EL_UNDEFINED,
1438     0,
1439     NULL,
1440     NULL,
1441     NULL,
1442   },
1443
1444   {
1445     EL_UNDEFINED,
1446     EL_UNDEFINED,
1447     -1,
1448     NULL,
1449     NULL,
1450     NULL
1451   }
1452 };
1453
1454 struct
1455 {
1456   int element;
1457   int push_delay_fixed, push_delay_random;
1458 }
1459 push_delay_list[] =
1460 {
1461   { EL_SPRING,                  0, 0 },
1462   { EL_BALLOON,                 0, 0 },
1463
1464   { EL_SOKOBAN_OBJECT,          2, 0 },
1465   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1466   { EL_SATELLITE,               2, 0 },
1467   { EL_SP_DISK_YELLOW,          2, 0 },
1468
1469   { EL_UNDEFINED,               0, 0 },
1470 };
1471
1472 struct
1473 {
1474   int element;
1475   int move_stepsize;
1476 }
1477 move_stepsize_list[] =
1478 {
1479   { EL_AMOEBA_DROP,             2 },
1480   { EL_AMOEBA_DROPPING,         2 },
1481   { EL_QUICKSAND_FILLING,       1 },
1482   { EL_QUICKSAND_EMPTYING,      1 },
1483   { EL_QUICKSAND_FAST_FILLING,  2 },
1484   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1485   { EL_MAGIC_WALL_FILLING,      2 },
1486   { EL_MAGIC_WALL_EMPTYING,     2 },
1487   { EL_BD_MAGIC_WALL_FILLING,   2 },
1488   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1489   { EL_DC_MAGIC_WALL_FILLING,   2 },
1490   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1491
1492   { EL_UNDEFINED,               0 },
1493 };
1494
1495 struct
1496 {
1497   int element;
1498   int count;
1499 }
1500 collect_count_list[] =
1501 {
1502   { EL_EMERALD,                 1 },
1503   { EL_BD_DIAMOND,              1 },
1504   { EL_EMERALD_YELLOW,          1 },
1505   { EL_EMERALD_RED,             1 },
1506   { EL_EMERALD_PURPLE,          1 },
1507   { EL_DIAMOND,                 3 },
1508   { EL_SP_INFOTRON,             1 },
1509   { EL_PEARL,                   5 },
1510   { EL_CRYSTAL,                 8 },
1511
1512   { EL_UNDEFINED,               0 },
1513 };
1514
1515 struct
1516 {
1517   int element;
1518   int direction;
1519 }
1520 access_direction_list[] =
1521 {
1522   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1523   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1524   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1525   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1526   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1527   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1528   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1529   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1530   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1531   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1532   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1533
1534   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1535   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1536   { EL_SP_PORT_UP,                                                   MV_DOWN },
1537   { EL_SP_PORT_DOWN,                                         MV_UP           },
1538   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1539   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1540   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1541   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1542   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1543   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1544   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1545   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1546   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1547   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1548   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1549   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1550   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1551   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1552   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1553
1554   { EL_UNDEFINED,                       MV_NONE                              }
1555 };
1556
1557 static struct XY xy_topdown[] =
1558 {
1559   {  0, -1 },
1560   { -1,  0 },
1561   { +1,  0 },
1562   {  0, +1 }
1563 };
1564
1565 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1566
1567 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1568 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1569 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1570                                  IS_JUST_CHANGING(x, y))
1571
1572 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1573
1574 // static variables for playfield scan mode (scanning forward or backward)
1575 static int playfield_scan_start_x = 0;
1576 static int playfield_scan_start_y = 0;
1577 static int playfield_scan_delta_x = 1;
1578 static int playfield_scan_delta_y = 1;
1579
1580 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1581                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1582                                      (y) += playfield_scan_delta_y)     \
1583                                 for ((x) = playfield_scan_start_x;      \
1584                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1585                                      (x) += playfield_scan_delta_x)
1586
1587 #ifdef DEBUG
1588 void DEBUG_SetMaximumDynamite(void)
1589 {
1590   int i;
1591
1592   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1593     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1594       local_player->inventory_element[local_player->inventory_size++] =
1595         EL_DYNAMITE;
1596 }
1597 #endif
1598
1599 static void InitPlayfieldScanModeVars(void)
1600 {
1601   if (game.use_reverse_scan_direction)
1602   {
1603     playfield_scan_start_x = lev_fieldx - 1;
1604     playfield_scan_start_y = lev_fieldy - 1;
1605
1606     playfield_scan_delta_x = -1;
1607     playfield_scan_delta_y = -1;
1608   }
1609   else
1610   {
1611     playfield_scan_start_x = 0;
1612     playfield_scan_start_y = 0;
1613
1614     playfield_scan_delta_x = 1;
1615     playfield_scan_delta_y = 1;
1616   }
1617 }
1618
1619 static void InitPlayfieldScanMode(int mode)
1620 {
1621   game.use_reverse_scan_direction =
1622     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1623
1624   InitPlayfieldScanModeVars();
1625 }
1626
1627 static int get_move_delay_from_stepsize(int move_stepsize)
1628 {
1629   move_stepsize =
1630     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1631
1632   // make sure that stepsize value is always a power of 2
1633   move_stepsize = (1 << log_2(move_stepsize));
1634
1635   return TILEX / move_stepsize;
1636 }
1637
1638 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1639                                boolean init_game)
1640 {
1641   int player_nr = player->index_nr;
1642   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1643   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1644
1645   // do no immediately change move delay -- the player might just be moving
1646   player->move_delay_value_next = move_delay;
1647
1648   // information if player can move must be set separately
1649   player->cannot_move = cannot_move;
1650
1651   if (init_game)
1652   {
1653     player->move_delay       = game.initial_move_delay[player_nr];
1654     player->move_delay_value = game.initial_move_delay_value[player_nr];
1655
1656     player->move_delay_value_next = -1;
1657
1658     player->move_delay_reset_counter = 0;
1659   }
1660 }
1661
1662 void GetPlayerConfig(void)
1663 {
1664   GameFrameDelay = setup.game_frame_delay;
1665
1666   if (!audio.sound_available)
1667     setup.sound_simple = FALSE;
1668
1669   if (!audio.loops_available)
1670     setup.sound_loops = FALSE;
1671
1672   if (!audio.music_available)
1673     setup.sound_music = FALSE;
1674
1675   if (!video.fullscreen_available)
1676     setup.fullscreen = FALSE;
1677
1678   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1679
1680   SetAudioMode(setup.sound);
1681 }
1682
1683 int GetElementFromGroupElement(int element)
1684 {
1685   if (IS_GROUP_ELEMENT(element))
1686   {
1687     struct ElementGroupInfo *group = element_info[element].group;
1688     int last_anim_random_frame = gfx.anim_random_frame;
1689     int element_pos;
1690
1691     if (group->choice_mode == ANIM_RANDOM)
1692       gfx.anim_random_frame = RND(group->num_elements_resolved);
1693
1694     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1695                                     group->choice_mode, 0,
1696                                     group->choice_pos);
1697
1698     if (group->choice_mode == ANIM_RANDOM)
1699       gfx.anim_random_frame = last_anim_random_frame;
1700
1701     group->choice_pos++;
1702
1703     element = group->element_resolved[element_pos];
1704   }
1705
1706   return element;
1707 }
1708
1709 static void IncrementSokobanFieldsNeeded(void)
1710 {
1711   if (level.sb_fields_needed)
1712     game.sokoban_fields_still_needed++;
1713 }
1714
1715 static void IncrementSokobanObjectsNeeded(void)
1716 {
1717   if (level.sb_objects_needed)
1718     game.sokoban_objects_still_needed++;
1719 }
1720
1721 static void DecrementSokobanFieldsNeeded(void)
1722 {
1723   if (game.sokoban_fields_still_needed > 0)
1724     game.sokoban_fields_still_needed--;
1725 }
1726
1727 static void DecrementSokobanObjectsNeeded(void)
1728 {
1729   if (game.sokoban_objects_still_needed > 0)
1730     game.sokoban_objects_still_needed--;
1731 }
1732
1733 static void InitPlayerField(int x, int y, int element, boolean init_game)
1734 {
1735   if (element == EL_SP_MURPHY)
1736   {
1737     if (init_game)
1738     {
1739       if (stored_player[0].present)
1740       {
1741         Tile[x][y] = EL_SP_MURPHY_CLONE;
1742
1743         return;
1744       }
1745       else
1746       {
1747         stored_player[0].initial_element = element;
1748         stored_player[0].use_murphy = TRUE;
1749
1750         if (!level.use_artwork_element[0])
1751           stored_player[0].artwork_element = EL_SP_MURPHY;
1752       }
1753
1754       Tile[x][y] = EL_PLAYER_1;
1755     }
1756   }
1757
1758   if (init_game)
1759   {
1760     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1761     int jx = player->jx, jy = player->jy;
1762
1763     player->present = TRUE;
1764
1765     player->block_last_field = (element == EL_SP_MURPHY ?
1766                                 level.sp_block_last_field :
1767                                 level.block_last_field);
1768
1769     // ---------- initialize player's last field block delay ------------------
1770
1771     // always start with reliable default value (no adjustment needed)
1772     player->block_delay_adjustment = 0;
1773
1774     // special case 1: in Supaplex, Murphy blocks last field one more frame
1775     if (player->block_last_field && element == EL_SP_MURPHY)
1776       player->block_delay_adjustment = 1;
1777
1778     // special case 2: in game engines before 3.1.1, blocking was different
1779     if (game.use_block_last_field_bug)
1780       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1781
1782     if (!network.enabled || player->connected_network)
1783     {
1784       player->active = TRUE;
1785
1786       // remove potentially duplicate players
1787       if (IN_LEV_FIELD(jx, jy) && StorePlayer[jx][jy] == Tile[x][y])
1788         StorePlayer[jx][jy] = 0;
1789
1790       StorePlayer[x][y] = Tile[x][y];
1791
1792 #if DEBUG_INIT_PLAYER
1793       Debug("game:init:player", "- player element %d activated",
1794             player->element_nr);
1795       Debug("game:init:player", "  (local player is %d and currently %s)",
1796             local_player->element_nr,
1797             local_player->active ? "active" : "not active");
1798     }
1799 #endif
1800
1801     Tile[x][y] = EL_EMPTY;
1802
1803     player->jx = player->last_jx = x;
1804     player->jy = player->last_jy = y;
1805   }
1806
1807   // always check if player was just killed and should be reanimated
1808   {
1809     int player_nr = GET_PLAYER_NR(element);
1810     struct PlayerInfo *player = &stored_player[player_nr];
1811
1812     if (player->active && player->killed)
1813       player->reanimated = TRUE; // if player was just killed, reanimate him
1814   }
1815 }
1816
1817 static void InitField(int x, int y, boolean init_game)
1818 {
1819   int element = Tile[x][y];
1820
1821   switch (element)
1822   {
1823     case EL_SP_MURPHY:
1824     case EL_PLAYER_1:
1825     case EL_PLAYER_2:
1826     case EL_PLAYER_3:
1827     case EL_PLAYER_4:
1828       InitPlayerField(x, y, element, init_game);
1829       break;
1830
1831     case EL_SOKOBAN_FIELD_PLAYER:
1832       element = Tile[x][y] = EL_PLAYER_1;
1833       InitField(x, y, init_game);
1834
1835       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1836       InitField(x, y, init_game);
1837       break;
1838
1839     case EL_SOKOBAN_FIELD_EMPTY:
1840       IncrementSokobanFieldsNeeded();
1841       break;
1842
1843     case EL_SOKOBAN_OBJECT:
1844       IncrementSokobanObjectsNeeded();
1845       break;
1846
1847     case EL_STONEBLOCK:
1848       if (x < lev_fieldx - 1 && Tile[x + 1][y] == EL_ACID)
1849         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1850       else if (x > 0 && Tile[x - 1][y] == EL_ACID)
1851         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1852       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPLEFT)
1853         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1854       else if (y > 0 && Tile[x][y - 1] == EL_ACID)
1855         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1856       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPRIGHT)
1857         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1858       break;
1859
1860     case EL_BUG:
1861     case EL_BUG_RIGHT:
1862     case EL_BUG_UP:
1863     case EL_BUG_LEFT:
1864     case EL_BUG_DOWN:
1865     case EL_SPACESHIP:
1866     case EL_SPACESHIP_RIGHT:
1867     case EL_SPACESHIP_UP:
1868     case EL_SPACESHIP_LEFT:
1869     case EL_SPACESHIP_DOWN:
1870     case EL_BD_BUTTERFLY:
1871     case EL_BD_BUTTERFLY_RIGHT:
1872     case EL_BD_BUTTERFLY_UP:
1873     case EL_BD_BUTTERFLY_LEFT:
1874     case EL_BD_BUTTERFLY_DOWN:
1875     case EL_BD_FIREFLY:
1876     case EL_BD_FIREFLY_RIGHT:
1877     case EL_BD_FIREFLY_UP:
1878     case EL_BD_FIREFLY_LEFT:
1879     case EL_BD_FIREFLY_DOWN:
1880     case EL_PACMAN_RIGHT:
1881     case EL_PACMAN_UP:
1882     case EL_PACMAN_LEFT:
1883     case EL_PACMAN_DOWN:
1884     case EL_YAMYAM:
1885     case EL_YAMYAM_LEFT:
1886     case EL_YAMYAM_RIGHT:
1887     case EL_YAMYAM_UP:
1888     case EL_YAMYAM_DOWN:
1889     case EL_DARK_YAMYAM:
1890     case EL_ROBOT:
1891     case EL_PACMAN:
1892     case EL_SP_SNIKSNAK:
1893     case EL_SP_ELECTRON:
1894     case EL_MOLE:
1895     case EL_MOLE_LEFT:
1896     case EL_MOLE_RIGHT:
1897     case EL_MOLE_UP:
1898     case EL_MOLE_DOWN:
1899     case EL_SPRING_LEFT:
1900     case EL_SPRING_RIGHT:
1901       InitMovDir(x, y);
1902       break;
1903
1904     case EL_AMOEBA_FULL:
1905     case EL_BD_AMOEBA:
1906       InitAmoebaNr(x, y);
1907       break;
1908
1909     case EL_AMOEBA_DROP:
1910       if (y == lev_fieldy - 1)
1911       {
1912         Tile[x][y] = EL_AMOEBA_GROWING;
1913         Store[x][y] = EL_AMOEBA_WET;
1914       }
1915       break;
1916
1917     case EL_DYNAMITE_ACTIVE:
1918     case EL_SP_DISK_RED_ACTIVE:
1919     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1920     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1921     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1922     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1923       MovDelay[x][y] = 96;
1924       break;
1925
1926     case EL_EM_DYNAMITE_ACTIVE:
1927       MovDelay[x][y] = 32;
1928       break;
1929
1930     case EL_LAMP:
1931       game.lights_still_needed++;
1932       break;
1933
1934     case EL_PENGUIN:
1935       game.friends_still_needed++;
1936       break;
1937
1938     case EL_PIG:
1939     case EL_DRAGON:
1940       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1941       break;
1942
1943     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1944     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1945     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1946     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1947     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1948     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1949     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1950     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1951     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1952     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1953     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1954     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1955       if (init_game)
1956       {
1957         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1958         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1959         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1960
1961         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1962         {
1963           game.belt_dir[belt_nr] = belt_dir;
1964           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1965         }
1966         else    // more than one switch -- set it like the first switch
1967         {
1968           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1969         }
1970       }
1971       break;
1972
1973     case EL_LIGHT_SWITCH_ACTIVE:
1974       if (init_game)
1975         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1976       break;
1977
1978     case EL_INVISIBLE_STEELWALL:
1979     case EL_INVISIBLE_WALL:
1980     case EL_INVISIBLE_SAND:
1981       if (game.light_time_left > 0 ||
1982           game.lenses_time_left > 0)
1983         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1984       break;
1985
1986     case EL_EMC_MAGIC_BALL:
1987       if (game.ball_active)
1988         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1989       break;
1990
1991     case EL_EMC_MAGIC_BALL_SWITCH:
1992       if (game.ball_active)
1993         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1994       break;
1995
1996     case EL_TRIGGER_PLAYER:
1997     case EL_TRIGGER_ELEMENT:
1998     case EL_TRIGGER_CE_VALUE:
1999     case EL_TRIGGER_CE_SCORE:
2000     case EL_SELF:
2001     case EL_ANY_ELEMENT:
2002     case EL_CURRENT_CE_VALUE:
2003     case EL_CURRENT_CE_SCORE:
2004     case EL_PREV_CE_1:
2005     case EL_PREV_CE_2:
2006     case EL_PREV_CE_3:
2007     case EL_PREV_CE_4:
2008     case EL_PREV_CE_5:
2009     case EL_PREV_CE_6:
2010     case EL_PREV_CE_7:
2011     case EL_PREV_CE_8:
2012     case EL_NEXT_CE_1:
2013     case EL_NEXT_CE_2:
2014     case EL_NEXT_CE_3:
2015     case EL_NEXT_CE_4:
2016     case EL_NEXT_CE_5:
2017     case EL_NEXT_CE_6:
2018     case EL_NEXT_CE_7:
2019     case EL_NEXT_CE_8:
2020       // reference elements should not be used on the playfield
2021       Tile[x][y] = EL_EMPTY;
2022       break;
2023
2024     default:
2025       if (IS_CUSTOM_ELEMENT(element))
2026       {
2027         if (CAN_MOVE(element))
2028           InitMovDir(x, y);
2029
2030         if (!element_info[element].use_last_ce_value || init_game)
2031           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2032       }
2033       else if (IS_GROUP_ELEMENT(element))
2034       {
2035         Tile[x][y] = GetElementFromGroupElement(element);
2036
2037         InitField(x, y, init_game);
2038       }
2039       else if (IS_EMPTY_ELEMENT(element))
2040       {
2041         GfxElementEmpty[x][y] = element;
2042         Tile[x][y] = EL_EMPTY;
2043
2044         if (element_info[element].use_gfx_element)
2045           game.use_masked_elements = TRUE;
2046       }
2047
2048       break;
2049   }
2050
2051   if (!init_game)
2052     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2053 }
2054
2055 static void InitField_WithBug1(int x, int y, boolean init_game)
2056 {
2057   InitField(x, y, init_game);
2058
2059   // not needed to call InitMovDir() -- already done by InitField()!
2060   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2061       CAN_MOVE(Tile[x][y]))
2062     InitMovDir(x, y);
2063 }
2064
2065 static void InitField_WithBug2(int x, int y, boolean init_game)
2066 {
2067   int old_element = Tile[x][y];
2068
2069   InitField(x, y, init_game);
2070
2071   // not needed to call InitMovDir() -- already done by InitField()!
2072   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2073       CAN_MOVE(old_element) &&
2074       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2075     InitMovDir(x, y);
2076
2077   /* this case is in fact a combination of not less than three bugs:
2078      first, it calls InitMovDir() for elements that can move, although this is
2079      already done by InitField(); then, it checks the element that was at this
2080      field _before_ the call to InitField() (which can change it); lastly, it
2081      was not called for "mole with direction" elements, which were treated as
2082      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2083   */
2084 }
2085
2086 static int get_key_element_from_nr(int key_nr)
2087 {
2088   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2089                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2090                           EL_EM_KEY_1 : EL_KEY_1);
2091
2092   return key_base_element + key_nr;
2093 }
2094
2095 static int get_next_dropped_element(struct PlayerInfo *player)
2096 {
2097   return (player->inventory_size > 0 ?
2098           player->inventory_element[player->inventory_size - 1] :
2099           player->inventory_infinite_element != EL_UNDEFINED ?
2100           player->inventory_infinite_element :
2101           player->dynabombs_left > 0 ?
2102           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2103           EL_UNDEFINED);
2104 }
2105
2106 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2107 {
2108   // pos >= 0: get element from bottom of the stack;
2109   // pos <  0: get element from top of the stack
2110
2111   if (pos < 0)
2112   {
2113     int min_inventory_size = -pos;
2114     int inventory_pos = player->inventory_size - min_inventory_size;
2115     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2116
2117     return (player->inventory_size >= min_inventory_size ?
2118             player->inventory_element[inventory_pos] :
2119             player->inventory_infinite_element != EL_UNDEFINED ?
2120             player->inventory_infinite_element :
2121             player->dynabombs_left >= min_dynabombs_left ?
2122             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2123             EL_UNDEFINED);
2124   }
2125   else
2126   {
2127     int min_dynabombs_left = pos + 1;
2128     int min_inventory_size = pos + 1 - player->dynabombs_left;
2129     int inventory_pos = pos - player->dynabombs_left;
2130
2131     return (player->inventory_infinite_element != EL_UNDEFINED ?
2132             player->inventory_infinite_element :
2133             player->dynabombs_left >= min_dynabombs_left ?
2134             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2135             player->inventory_size >= min_inventory_size ?
2136             player->inventory_element[inventory_pos] :
2137             EL_UNDEFINED);
2138   }
2139 }
2140
2141 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2142 {
2143   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2144   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2145   int compare_result;
2146
2147   if (gpo1->sort_priority != gpo2->sort_priority)
2148     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2149   else
2150     compare_result = gpo1->nr - gpo2->nr;
2151
2152   return compare_result;
2153 }
2154
2155 int getPlayerInventorySize(int player_nr)
2156 {
2157   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2158     return game_em.ply[player_nr]->dynamite;
2159   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2160     return game_sp.red_disk_count;
2161   else
2162     return stored_player[player_nr].inventory_size;
2163 }
2164
2165 static void InitGameControlValues(void)
2166 {
2167   int i;
2168
2169   for (i = 0; game_panel_controls[i].nr != -1; i++)
2170   {
2171     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2172     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2173     struct TextPosInfo *pos = gpc->pos;
2174     int nr = gpc->nr;
2175     int type = gpc->type;
2176
2177     if (nr != i)
2178     {
2179       Error("'game_panel_controls' structure corrupted at %d", i);
2180
2181       Fail("this should not happen -- please debug");
2182     }
2183
2184     // force update of game controls after initialization
2185     gpc->value = gpc->last_value = -1;
2186     gpc->frame = gpc->last_frame = -1;
2187     gpc->gfx_frame = -1;
2188
2189     // determine panel value width for later calculation of alignment
2190     if (type == TYPE_INTEGER || type == TYPE_STRING)
2191     {
2192       pos->width = pos->size * getFontWidth(pos->font);
2193       pos->height = getFontHeight(pos->font);
2194     }
2195     else if (type == TYPE_ELEMENT)
2196     {
2197       pos->width = pos->size;
2198       pos->height = pos->size;
2199     }
2200
2201     // fill structure for game panel draw order
2202     gpo->nr = gpc->nr;
2203     gpo->sort_priority = pos->sort_priority;
2204   }
2205
2206   // sort game panel controls according to sort_priority and control number
2207   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2208         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2209 }
2210
2211 static void UpdatePlayfieldElementCount(void)
2212 {
2213   boolean use_element_count = FALSE;
2214   int i, j, x, y;
2215
2216   // first check if it is needed at all to calculate playfield element count
2217   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2218     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2219       use_element_count = TRUE;
2220
2221   if (!use_element_count)
2222     return;
2223
2224   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2225     element_info[i].element_count = 0;
2226
2227   SCAN_PLAYFIELD(x, y)
2228   {
2229     element_info[Tile[x][y]].element_count++;
2230   }
2231
2232   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2233     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2234       if (IS_IN_GROUP(j, i))
2235         element_info[EL_GROUP_START + i].element_count +=
2236           element_info[j].element_count;
2237 }
2238
2239 static void UpdateGameControlValues(void)
2240 {
2241   int i, k;
2242   int time = (game.LevelSolved ?
2243               game.LevelSolved_CountingTime :
2244               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2245               game_em.lev->time :
2246               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2247               game_sp.time_played :
2248               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2249               game_mm.energy_left :
2250               game.no_level_time_limit ? TimePlayed : TimeLeft);
2251   int score = (game.LevelSolved ?
2252                game.LevelSolved_CountingScore :
2253                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2254                game_em.lev->score :
2255                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2256                game_sp.score :
2257                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2258                game_mm.score :
2259                game.score);
2260   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2261               game_em.lev->gems_needed :
2262               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2263               game_sp.infotrons_still_needed :
2264               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2265               game_mm.kettles_still_needed :
2266               game.gems_still_needed);
2267   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2268                      game_em.lev->gems_needed > 0 :
2269                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2270                      game_sp.infotrons_still_needed > 0 :
2271                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2272                      game_mm.kettles_still_needed > 0 ||
2273                      game_mm.lights_still_needed > 0 :
2274                      game.gems_still_needed > 0 ||
2275                      game.sokoban_fields_still_needed > 0 ||
2276                      game.sokoban_objects_still_needed > 0 ||
2277                      game.lights_still_needed > 0);
2278   int health = (game.LevelSolved ?
2279                 game.LevelSolved_CountingHealth :
2280                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2281                 MM_HEALTH(game_mm.laser_overload_value) :
2282                 game.health);
2283   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2284
2285   UpdatePlayfieldElementCount();
2286
2287   // update game panel control values
2288
2289   // used instead of "level_nr" (for network games)
2290   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2291   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2292
2293   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2294   for (i = 0; i < MAX_NUM_KEYS; i++)
2295     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2296   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2297   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2298
2299   if (game.centered_player_nr == -1)
2300   {
2301     for (i = 0; i < MAX_PLAYERS; i++)
2302     {
2303       // only one player in Supaplex game engine
2304       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2305         break;
2306
2307       for (k = 0; k < MAX_NUM_KEYS; k++)
2308       {
2309         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2310         {
2311           if (game_em.ply[i]->keys & (1 << k))
2312             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2313               get_key_element_from_nr(k);
2314         }
2315         else if (stored_player[i].key[k])
2316           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2317             get_key_element_from_nr(k);
2318       }
2319
2320       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2321         getPlayerInventorySize(i);
2322
2323       if (stored_player[i].num_white_keys > 0)
2324         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2325           EL_DC_KEY_WHITE;
2326
2327       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2328         stored_player[i].num_white_keys;
2329     }
2330   }
2331   else
2332   {
2333     int player_nr = game.centered_player_nr;
2334
2335     for (k = 0; k < MAX_NUM_KEYS; k++)
2336     {
2337       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2338       {
2339         if (game_em.ply[player_nr]->keys & (1 << k))
2340           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2341             get_key_element_from_nr(k);
2342       }
2343       else if (stored_player[player_nr].key[k])
2344         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2345           get_key_element_from_nr(k);
2346     }
2347
2348     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2349       getPlayerInventorySize(player_nr);
2350
2351     if (stored_player[player_nr].num_white_keys > 0)
2352       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2353
2354     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2355       stored_player[player_nr].num_white_keys;
2356   }
2357
2358   // re-arrange keys on game panel, if needed or if defined by style settings
2359   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2360   {
2361     int nr = GAME_PANEL_KEY_1 + i;
2362     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2363     struct TextPosInfo *pos = gpc->pos;
2364
2365     // skip check if key is not in the player's inventory
2366     if (gpc->value == EL_EMPTY)
2367       continue;
2368
2369     // check if keys should be arranged on panel from left to right
2370     if (pos->style == STYLE_LEFTMOST_POSITION)
2371     {
2372       // check previous key positions (left from current key)
2373       for (k = 0; k < i; k++)
2374       {
2375         int nr_new = GAME_PANEL_KEY_1 + k;
2376
2377         if (game_panel_controls[nr_new].value == EL_EMPTY)
2378         {
2379           game_panel_controls[nr_new].value = gpc->value;
2380           gpc->value = EL_EMPTY;
2381
2382           break;
2383         }
2384       }
2385     }
2386
2387     // check if "undefined" keys can be placed at some other position
2388     if (pos->x == -1 && pos->y == -1)
2389     {
2390       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2391
2392       // 1st try: display key at the same position as normal or EM keys
2393       if (game_panel_controls[nr_new].value == EL_EMPTY)
2394       {
2395         game_panel_controls[nr_new].value = gpc->value;
2396       }
2397       else
2398       {
2399         // 2nd try: display key at the next free position in the key panel
2400         for (k = 0; k < STD_NUM_KEYS; k++)
2401         {
2402           nr_new = GAME_PANEL_KEY_1 + k;
2403
2404           if (game_panel_controls[nr_new].value == EL_EMPTY)
2405           {
2406             game_panel_controls[nr_new].value = gpc->value;
2407
2408             break;
2409           }
2410         }
2411       }
2412     }
2413   }
2414
2415   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2416   {
2417     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2418       get_inventory_element_from_pos(local_player, i);
2419     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2420       get_inventory_element_from_pos(local_player, -i - 1);
2421   }
2422
2423   game_panel_controls[GAME_PANEL_SCORE].value = score;
2424   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2425
2426   game_panel_controls[GAME_PANEL_TIME].value = time;
2427
2428   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2429   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2430   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2431
2432   if (level.time == 0)
2433     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2434   else
2435     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2436
2437   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2438   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2439
2440   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2441
2442   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2443     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2444      EL_EMPTY);
2445   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2446     local_player->shield_normal_time_left;
2447   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2448     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2449      EL_EMPTY);
2450   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2451     local_player->shield_deadly_time_left;
2452
2453   game_panel_controls[GAME_PANEL_EXIT].value =
2454     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2455
2456   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2457     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2458   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2459     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2460      EL_EMC_MAGIC_BALL_SWITCH);
2461
2462   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2463     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2464   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2465     game.light_time_left;
2466
2467   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2468     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2469   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2470     game.timegate_time_left;
2471
2472   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2473     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2474
2475   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2476     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2477   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2478     game.lenses_time_left;
2479
2480   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2481     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2482   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2483     game.magnify_time_left;
2484
2485   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2486     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2487      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2488      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2489      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2490      EL_BALLOON_SWITCH_NONE);
2491
2492   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2493     local_player->dynabomb_count;
2494   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2495     local_player->dynabomb_size;
2496   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2497     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2498
2499   game_panel_controls[GAME_PANEL_PENGUINS].value =
2500     game.friends_still_needed;
2501
2502   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2503     game.sokoban_objects_still_needed;
2504   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2505     game.sokoban_fields_still_needed;
2506
2507   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2508     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2509
2510   for (i = 0; i < NUM_BELTS; i++)
2511   {
2512     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2513       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2514        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2515     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2516       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2517   }
2518
2519   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2520     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2521   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2522     game.magic_wall_time_left;
2523
2524   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2525     local_player->gravity;
2526
2527   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2528     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2529
2530   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2531     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2532       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2533        game.panel.element[i].id : EL_UNDEFINED);
2534
2535   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2536     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2537       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2538        element_info[game.panel.element_count[i].id].element_count : 0);
2539
2540   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2541     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2542       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2543        element_info[game.panel.ce_score[i].id].collect_score : 0);
2544
2545   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2546     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2547       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2548        element_info[game.panel.ce_score_element[i].id].collect_score :
2549        EL_UNDEFINED);
2550
2551   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2552   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2553   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2554
2555   // update game panel control frames
2556
2557   for (i = 0; game_panel_controls[i].nr != -1; i++)
2558   {
2559     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2560
2561     if (gpc->type == TYPE_ELEMENT)
2562     {
2563       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2564       {
2565         int last_anim_random_frame = gfx.anim_random_frame;
2566         int element = gpc->value;
2567         int graphic = el2panelimg(element);
2568         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2569                                sync_random_frame :
2570                                graphic_info[graphic].anim_global_anim_sync ?
2571                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2572
2573         if (gpc->value != gpc->last_value)
2574         {
2575           gpc->gfx_frame = 0;
2576           gpc->gfx_random = init_gfx_random;
2577         }
2578         else
2579         {
2580           gpc->gfx_frame++;
2581
2582           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2583               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2584             gpc->gfx_random = init_gfx_random;
2585         }
2586
2587         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2588           gfx.anim_random_frame = gpc->gfx_random;
2589
2590         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2591           gpc->gfx_frame = element_info[element].collect_score;
2592
2593         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2594
2595         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2596           gfx.anim_random_frame = last_anim_random_frame;
2597       }
2598     }
2599     else if (gpc->type == TYPE_GRAPHIC)
2600     {
2601       if (gpc->graphic != IMG_UNDEFINED)
2602       {
2603         int last_anim_random_frame = gfx.anim_random_frame;
2604         int graphic = gpc->graphic;
2605         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2606                                sync_random_frame :
2607                                graphic_info[graphic].anim_global_anim_sync ?
2608                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2609
2610         if (gpc->value != gpc->last_value)
2611         {
2612           gpc->gfx_frame = 0;
2613           gpc->gfx_random = init_gfx_random;
2614         }
2615         else
2616         {
2617           gpc->gfx_frame++;
2618
2619           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2620               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2621             gpc->gfx_random = init_gfx_random;
2622         }
2623
2624         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2625           gfx.anim_random_frame = gpc->gfx_random;
2626
2627         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2628
2629         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2630           gfx.anim_random_frame = last_anim_random_frame;
2631       }
2632     }
2633   }
2634 }
2635
2636 static void DisplayGameControlValues(void)
2637 {
2638   boolean redraw_panel = FALSE;
2639   int i;
2640
2641   for (i = 0; game_panel_controls[i].nr != -1; i++)
2642   {
2643     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2644
2645     if (PANEL_DEACTIVATED(gpc->pos))
2646       continue;
2647
2648     if (gpc->value == gpc->last_value &&
2649         gpc->frame == gpc->last_frame)
2650       continue;
2651
2652     redraw_panel = TRUE;
2653   }
2654
2655   if (!redraw_panel)
2656     return;
2657
2658   // copy default game door content to main double buffer
2659
2660   // !!! CHECK AGAIN !!!
2661   SetPanelBackground();
2662   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2663   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2664
2665   // redraw game control buttons
2666   RedrawGameButtons();
2667
2668   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2669
2670   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2671   {
2672     int nr = game_panel_order[i].nr;
2673     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2674     struct TextPosInfo *pos = gpc->pos;
2675     int type = gpc->type;
2676     int value = gpc->value;
2677     int frame = gpc->frame;
2678     int size = pos->size;
2679     int font = pos->font;
2680     boolean draw_masked = pos->draw_masked;
2681     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2682
2683     if (PANEL_DEACTIVATED(pos))
2684       continue;
2685
2686     if (pos->class == get_hash_from_key("extra_panel_items") &&
2687         !setup.prefer_extra_panel_items)
2688       continue;
2689
2690     gpc->last_value = value;
2691     gpc->last_frame = frame;
2692
2693     if (type == TYPE_INTEGER)
2694     {
2695       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2696           nr == GAME_PANEL_INVENTORY_COUNT ||
2697           nr == GAME_PANEL_SCORE ||
2698           nr == GAME_PANEL_HIGHSCORE ||
2699           nr == GAME_PANEL_TIME)
2700       {
2701         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2702
2703         if (use_dynamic_size)           // use dynamic number of digits
2704         {
2705           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 :
2706                               nr == GAME_PANEL_INVENTORY_COUNT ||
2707                               nr == GAME_PANEL_TIME ? 1000 : 100000);
2708           int size_add = (nr == GAME_PANEL_LEVEL_NUMBER ||
2709                           nr == GAME_PANEL_INVENTORY_COUNT ||
2710                           nr == GAME_PANEL_TIME ? 1 : 2);
2711           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 :
2712                        nr == GAME_PANEL_INVENTORY_COUNT ||
2713                        nr == GAME_PANEL_TIME ? 3 : 5);
2714           int size2 = size1 + size_add;
2715           int font1 = pos->font;
2716           int font2 = pos->font_alt;
2717
2718           size = (value < value_change ? size1 : size2);
2719           font = (value < value_change ? font1 : font2);
2720         }
2721       }
2722
2723       // correct text size if "digits" is zero or less
2724       if (size <= 0)
2725         size = strlen(int2str(value, size));
2726
2727       // dynamically correct text alignment
2728       pos->width = size * getFontWidth(font);
2729
2730       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2731                   int2str(value, size), font, mask_mode);
2732     }
2733     else if (type == TYPE_ELEMENT)
2734     {
2735       int element, graphic;
2736       Bitmap *src_bitmap;
2737       int src_x, src_y;
2738       int width, height;
2739       int dst_x = PANEL_XPOS(pos);
2740       int dst_y = PANEL_YPOS(pos);
2741
2742       if (value != EL_UNDEFINED && value != EL_EMPTY)
2743       {
2744         element = value;
2745         graphic = el2panelimg(value);
2746
2747 #if 0
2748         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2749               element, EL_NAME(element), size);
2750 #endif
2751
2752         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2753           size = TILESIZE;
2754
2755         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2756                               &src_x, &src_y);
2757
2758         width  = graphic_info[graphic].width  * size / TILESIZE;
2759         height = graphic_info[graphic].height * size / TILESIZE;
2760
2761         if (draw_masked)
2762           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2763                            dst_x, dst_y);
2764         else
2765           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2766                      dst_x, dst_y);
2767       }
2768     }
2769     else if (type == TYPE_GRAPHIC)
2770     {
2771       int graphic        = gpc->graphic;
2772       int graphic_active = gpc->graphic_active;
2773       Bitmap *src_bitmap;
2774       int src_x, src_y;
2775       int width, height;
2776       int dst_x = PANEL_XPOS(pos);
2777       int dst_y = PANEL_YPOS(pos);
2778       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2779                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2780
2781       if (graphic != IMG_UNDEFINED && !skip)
2782       {
2783         if (pos->style == STYLE_REVERSE)
2784           value = 100 - value;
2785
2786         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2787
2788         if (pos->direction & MV_HORIZONTAL)
2789         {
2790           width  = graphic_info[graphic_active].width * value / 100;
2791           height = graphic_info[graphic_active].height;
2792
2793           if (pos->direction == MV_LEFT)
2794           {
2795             src_x += graphic_info[graphic_active].width - width;
2796             dst_x += graphic_info[graphic_active].width - width;
2797           }
2798         }
2799         else
2800         {
2801           width  = graphic_info[graphic_active].width;
2802           height = graphic_info[graphic_active].height * value / 100;
2803
2804           if (pos->direction == MV_UP)
2805           {
2806             src_y += graphic_info[graphic_active].height - height;
2807             dst_y += graphic_info[graphic_active].height - height;
2808           }
2809         }
2810
2811         if (draw_masked)
2812           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2813                            dst_x, dst_y);
2814         else
2815           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2816                      dst_x, dst_y);
2817
2818         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2819
2820         if (pos->direction & MV_HORIZONTAL)
2821         {
2822           if (pos->direction == MV_RIGHT)
2823           {
2824             src_x += width;
2825             dst_x += width;
2826           }
2827           else
2828           {
2829             dst_x = PANEL_XPOS(pos);
2830           }
2831
2832           width = graphic_info[graphic].width - width;
2833         }
2834         else
2835         {
2836           if (pos->direction == MV_DOWN)
2837           {
2838             src_y += height;
2839             dst_y += height;
2840           }
2841           else
2842           {
2843             dst_y = PANEL_YPOS(pos);
2844           }
2845
2846           height = graphic_info[graphic].height - height;
2847         }
2848
2849         if (draw_masked)
2850           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2851                            dst_x, dst_y);
2852         else
2853           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2854                      dst_x, dst_y);
2855       }
2856     }
2857     else if (type == TYPE_STRING)
2858     {
2859       boolean active = (value != 0);
2860       char *state_normal = "off";
2861       char *state_active = "on";
2862       char *state = (active ? state_active : state_normal);
2863       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2864                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2865                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2866                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2867
2868       if (nr == GAME_PANEL_GRAVITY_STATE)
2869       {
2870         int font1 = pos->font;          // (used for normal state)
2871         int font2 = pos->font_alt;      // (used for active state)
2872
2873         font = (active ? font2 : font1);
2874       }
2875
2876       if (s != NULL)
2877       {
2878         char *s_cut;
2879
2880         if (size <= 0)
2881         {
2882           // don't truncate output if "chars" is zero or less
2883           size = strlen(s);
2884
2885           // dynamically correct text alignment
2886           pos->width = size * getFontWidth(font);
2887         }
2888
2889         s_cut = getStringCopyN(s, size);
2890
2891         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2892                     s_cut, font, mask_mode);
2893
2894         free(s_cut);
2895       }
2896     }
2897
2898     redraw_mask |= REDRAW_DOOR_1;
2899   }
2900
2901   SetGameStatus(GAME_MODE_PLAYING);
2902 }
2903
2904 void UpdateAndDisplayGameControlValues(void)
2905 {
2906   if (tape.deactivate_display)
2907     return;
2908
2909   UpdateGameControlValues();
2910   DisplayGameControlValues();
2911 }
2912
2913 void UpdateGameDoorValues(void)
2914 {
2915   UpdateGameControlValues();
2916 }
2917
2918 void DrawGameDoorValues(void)
2919 {
2920   DisplayGameControlValues();
2921 }
2922
2923
2924 // ============================================================================
2925 // InitGameEngine()
2926 // ----------------------------------------------------------------------------
2927 // initialize game engine due to level / tape version number
2928 // ============================================================================
2929
2930 static void InitGameEngine(void)
2931 {
2932   int i, j, k, l, x, y;
2933
2934   // set game engine from tape file when re-playing, else from level file
2935   game.engine_version = (tape.playing ? tape.engine_version :
2936                          level.game_version);
2937
2938   // set single or multi-player game mode (needed for re-playing tapes)
2939   game.team_mode = setup.team_mode;
2940
2941   if (tape.playing)
2942   {
2943     int num_players = 0;
2944
2945     for (i = 0; i < MAX_PLAYERS; i++)
2946       if (tape.player_participates[i])
2947         num_players++;
2948
2949     // multi-player tapes contain input data for more than one player
2950     game.team_mode = (num_players > 1);
2951   }
2952
2953 #if 0
2954   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2955         level.game_version);
2956   Debug("game:init:level", "          tape.file_version   == %06d",
2957         tape.file_version);
2958   Debug("game:init:level", "          tape.game_version   == %06d",
2959         tape.game_version);
2960   Debug("game:init:level", "          tape.engine_version == %06d",
2961         tape.engine_version);
2962   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2963         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2964 #endif
2965
2966   // --------------------------------------------------------------------------
2967   // set flags for bugs and changes according to active game engine version
2968   // --------------------------------------------------------------------------
2969
2970   /*
2971     Summary of bugfix:
2972     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2973
2974     Bug was introduced in version:
2975     2.0.1
2976
2977     Bug was fixed in version:
2978     4.2.0.0
2979
2980     Description:
2981     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2982     but the property "can fall" was missing, which caused some levels to be
2983     unsolvable. This was fixed in version 4.2.0.0.
2984
2985     Affected levels/tapes:
2986     An example for a tape that was fixed by this bugfix is tape 029 from the
2987     level set "rnd_sam_bateman".
2988     The wrong behaviour will still be used for all levels or tapes that were
2989     created/recorded with it. An example for this is tape 023 from the level
2990     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2991   */
2992
2993   boolean use_amoeba_dropping_cannot_fall_bug =
2994     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2995       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2996      (tape.playing &&
2997       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2998       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2999
3000   /*
3001     Summary of bugfix/change:
3002     Fixed move speed of elements entering or leaving magic wall.
3003
3004     Fixed/changed in version:
3005     2.0.1
3006
3007     Description:
3008     Before 2.0.1, move speed of elements entering or leaving magic wall was
3009     twice as fast as it is now.
3010     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
3011
3012     Affected levels/tapes:
3013     The first condition is generally needed for all levels/tapes before version
3014     2.0.1, which might use the old behaviour before it was changed; known tapes
3015     that are affected: Tape 014 from the level set "rnd_conor_mancone".
3016     The second condition is an exception from the above case and is needed for
3017     the special case of tapes recorded with game (not engine!) version 2.0.1 or
3018     above, but before it was known that this change would break tapes like the
3019     above and was fixed in 4.2.0.0, so that the changed behaviour was active
3020     although the engine version while recording maybe was before 2.0.1. There
3021     are a lot of tapes that are affected by this exception, like tape 006 from
3022     the level set "rnd_conor_mancone".
3023   */
3024
3025   boolean use_old_move_stepsize_for_magic_wall =
3026     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
3027      !(tape.playing &&
3028        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3029        tape.game_version <  VERSION_IDENT(4,2,0,0)));
3030
3031   /*
3032     Summary of bugfix/change:
3033     Fixed handling for custom elements that change when pushed by the player.
3034
3035     Fixed/changed in version:
3036     3.1.0
3037
3038     Description:
3039     Before 3.1.0, custom elements that "change when pushing" changed directly
3040     after the player started pushing them (until then handled in "DigField()").
3041     Since 3.1.0, these custom elements are not changed until the "pushing"
3042     move of the element is finished (now handled in "ContinueMoving()").
3043
3044     Affected levels/tapes:
3045     The first condition is generally needed for all levels/tapes before version
3046     3.1.0, which might use the old behaviour before it was changed; known tapes
3047     that are affected are some tapes from the level set "Walpurgis Gardens" by
3048     Jamie Cullen.
3049     The second condition is an exception from the above case and is needed for
3050     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3051     above (including some development versions of 3.1.0), but before it was
3052     known that this change would break tapes like the above and was fixed in
3053     3.1.1, so that the changed behaviour was active although the engine version
3054     while recording maybe was before 3.1.0. There is at least one tape that is
3055     affected by this exception, which is the tape for the one-level set "Bug
3056     Machine" by Juergen Bonhagen.
3057   */
3058
3059   game.use_change_when_pushing_bug =
3060     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3061      !(tape.playing &&
3062        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3063        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3064
3065   /*
3066     Summary of bugfix/change:
3067     Fixed handling for blocking the field the player leaves when moving.
3068
3069     Fixed/changed in version:
3070     3.1.1
3071
3072     Description:
3073     Before 3.1.1, when "block last field when moving" was enabled, the field
3074     the player is leaving when moving was blocked for the time of the move,
3075     and was directly unblocked afterwards. This resulted in the last field
3076     being blocked for exactly one less than the number of frames of one player
3077     move. Additionally, even when blocking was disabled, the last field was
3078     blocked for exactly one frame.
3079     Since 3.1.1, due to changes in player movement handling, the last field
3080     is not blocked at all when blocking is disabled. When blocking is enabled,
3081     the last field is blocked for exactly the number of frames of one player
3082     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3083     last field is blocked for exactly one more than the number of frames of
3084     one player move.
3085
3086     Affected levels/tapes:
3087     (!!! yet to be determined -- probably many !!!)
3088   */
3089
3090   game.use_block_last_field_bug =
3091     (game.engine_version < VERSION_IDENT(3,1,1,0));
3092
3093   /* various special flags and settings for native Emerald Mine game engine */
3094
3095   game_em.use_single_button =
3096     (game.engine_version > VERSION_IDENT(4,0,0,2));
3097
3098   game_em.use_snap_key_bug =
3099     (game.engine_version < VERSION_IDENT(4,0,1,0));
3100
3101   game_em.use_random_bug =
3102     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3103
3104   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3105
3106   game_em.use_old_explosions            = use_old_em_engine;
3107   game_em.use_old_android               = use_old_em_engine;
3108   game_em.use_old_push_elements         = use_old_em_engine;
3109   game_em.use_old_push_into_acid        = use_old_em_engine;
3110
3111   game_em.use_wrap_around               = !use_old_em_engine;
3112
3113   // --------------------------------------------------------------------------
3114
3115   // set maximal allowed number of custom element changes per game frame
3116   game.max_num_changes_per_frame = 1;
3117
3118   // default scan direction: scan playfield from top/left to bottom/right
3119   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3120
3121   // dynamically adjust element properties according to game engine version
3122   InitElementPropertiesEngine(game.engine_version);
3123
3124   // ---------- initialize special element properties -------------------------
3125
3126   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3127   if (use_amoeba_dropping_cannot_fall_bug)
3128     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3129
3130   // ---------- initialize player's initial move delay ------------------------
3131
3132   // dynamically adjust player properties according to level information
3133   for (i = 0; i < MAX_PLAYERS; i++)
3134     game.initial_move_delay_value[i] =
3135       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3136
3137   // dynamically adjust player properties according to game engine version
3138   for (i = 0; i < MAX_PLAYERS; i++)
3139     game.initial_move_delay[i] =
3140       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3141        game.initial_move_delay_value[i] : 0);
3142
3143   // ---------- initialize player's initial push delay ------------------------
3144
3145   // dynamically adjust player properties according to game engine version
3146   game.initial_push_delay_value =
3147     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3148
3149   // ---------- initialize changing elements ----------------------------------
3150
3151   // initialize changing elements information
3152   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3153   {
3154     struct ElementInfo *ei = &element_info[i];
3155
3156     // this pointer might have been changed in the level editor
3157     ei->change = &ei->change_page[0];
3158
3159     if (!IS_CUSTOM_ELEMENT(i))
3160     {
3161       ei->change->target_element = EL_EMPTY_SPACE;
3162       ei->change->delay_fixed = 0;
3163       ei->change->delay_random = 0;
3164       ei->change->delay_frames = 1;
3165     }
3166
3167     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3168     {
3169       ei->has_change_event[j] = FALSE;
3170
3171       ei->event_page_nr[j] = 0;
3172       ei->event_page[j] = &ei->change_page[0];
3173     }
3174   }
3175
3176   // add changing elements from pre-defined list
3177   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3178   {
3179     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3180     struct ElementInfo *ei = &element_info[ch_delay->element];
3181
3182     ei->change->target_element       = ch_delay->target_element;
3183     ei->change->delay_fixed          = ch_delay->change_delay;
3184
3185     ei->change->pre_change_function  = ch_delay->pre_change_function;
3186     ei->change->change_function      = ch_delay->change_function;
3187     ei->change->post_change_function = ch_delay->post_change_function;
3188
3189     ei->change->can_change = TRUE;
3190     ei->change->can_change_or_has_action = TRUE;
3191
3192     ei->has_change_event[CE_DELAY] = TRUE;
3193
3194     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3195     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3196   }
3197
3198   // ---------- initialize if element can trigger global animations -----------
3199
3200   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3201   {
3202     struct ElementInfo *ei = &element_info[i];
3203
3204     ei->has_anim_event = FALSE;
3205   }
3206
3207   InitGlobalAnimEventsForCustomElements();
3208
3209   // ---------- initialize internal run-time variables ------------------------
3210
3211   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3212   {
3213     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3214
3215     for (j = 0; j < ei->num_change_pages; j++)
3216     {
3217       ei->change_page[j].can_change_or_has_action =
3218         (ei->change_page[j].can_change |
3219          ei->change_page[j].has_action);
3220     }
3221   }
3222
3223   // add change events from custom element configuration
3224   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3225   {
3226     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3227
3228     for (j = 0; j < ei->num_change_pages; j++)
3229     {
3230       if (!ei->change_page[j].can_change_or_has_action)
3231         continue;
3232
3233       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3234       {
3235         // only add event page for the first page found with this event
3236         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3237         {
3238           ei->has_change_event[k] = TRUE;
3239
3240           ei->event_page_nr[k] = j;
3241           ei->event_page[k] = &ei->change_page[j];
3242         }
3243       }
3244     }
3245   }
3246
3247   // ---------- initialize reference elements in change conditions ------------
3248
3249   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3250   {
3251     int element = EL_CUSTOM_START + i;
3252     struct ElementInfo *ei = &element_info[element];
3253
3254     for (j = 0; j < ei->num_change_pages; j++)
3255     {
3256       int trigger_element = ei->change_page[j].initial_trigger_element;
3257
3258       if (trigger_element >= EL_PREV_CE_8 &&
3259           trigger_element <= EL_NEXT_CE_8)
3260         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3261
3262       ei->change_page[j].trigger_element = trigger_element;
3263     }
3264   }
3265
3266   // ---------- initialize run-time trigger player and element ----------------
3267
3268   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3269   {
3270     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3271
3272     for (j = 0; j < ei->num_change_pages; j++)
3273     {
3274       struct ElementChangeInfo *change = &ei->change_page[j];
3275
3276       change->actual_trigger_element = EL_EMPTY;
3277       change->actual_trigger_player = EL_EMPTY;
3278       change->actual_trigger_player_bits = CH_PLAYER_NONE;
3279       change->actual_trigger_side = CH_SIDE_NONE;
3280       change->actual_trigger_ce_value = 0;
3281       change->actual_trigger_ce_score = 0;
3282     }
3283   }
3284
3285   // ---------- initialize trigger events -------------------------------------
3286
3287   // initialize trigger events information
3288   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3289     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3290       trigger_events[i][j] = FALSE;
3291
3292   // add trigger events from element change event properties
3293   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3294   {
3295     struct ElementInfo *ei = &element_info[i];
3296
3297     for (j = 0; j < ei->num_change_pages; j++)
3298     {
3299       struct ElementChangeInfo *change = &ei->change_page[j];
3300
3301       if (!change->can_change_or_has_action)
3302         continue;
3303
3304       if (change->has_event[CE_BY_OTHER_ACTION])
3305       {
3306         int trigger_element = change->trigger_element;
3307
3308         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3309         {
3310           if (change->has_event[k])
3311           {
3312             if (IS_GROUP_ELEMENT(trigger_element))
3313             {
3314               struct ElementGroupInfo *group =
3315                 element_info[trigger_element].group;
3316
3317               for (l = 0; l < group->num_elements_resolved; l++)
3318                 trigger_events[group->element_resolved[l]][k] = TRUE;
3319             }
3320             else if (trigger_element == EL_ANY_ELEMENT)
3321               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3322                 trigger_events[l][k] = TRUE;
3323             else
3324               trigger_events[trigger_element][k] = TRUE;
3325           }
3326         }
3327       }
3328     }
3329   }
3330
3331   // ---------- initialize push delay -----------------------------------------
3332
3333   // initialize push delay values to default
3334   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3335   {
3336     if (!IS_CUSTOM_ELEMENT(i))
3337     {
3338       // set default push delay values (corrected since version 3.0.7-1)
3339       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3340       {
3341         element_info[i].push_delay_fixed = 2;
3342         element_info[i].push_delay_random = 8;
3343       }
3344       else
3345       {
3346         element_info[i].push_delay_fixed = 8;
3347         element_info[i].push_delay_random = 8;
3348       }
3349     }
3350   }
3351
3352   // set push delay value for certain elements from pre-defined list
3353   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3354   {
3355     int e = push_delay_list[i].element;
3356
3357     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3358     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3359   }
3360
3361   // set push delay value for Supaplex elements for newer engine versions
3362   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3363   {
3364     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3365     {
3366       if (IS_SP_ELEMENT(i))
3367       {
3368         // set SP push delay to just enough to push under a falling zonk
3369         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3370
3371         element_info[i].push_delay_fixed  = delay;
3372         element_info[i].push_delay_random = 0;
3373       }
3374     }
3375   }
3376
3377   // ---------- initialize move stepsize --------------------------------------
3378
3379   // initialize move stepsize values to default
3380   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3381     if (!IS_CUSTOM_ELEMENT(i))
3382       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3383
3384   // set move stepsize value for certain elements from pre-defined list
3385   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3386   {
3387     int e = move_stepsize_list[i].element;
3388
3389     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3390
3391     // set move stepsize value for certain elements for older engine versions
3392     if (use_old_move_stepsize_for_magic_wall)
3393     {
3394       if (e == EL_MAGIC_WALL_FILLING ||
3395           e == EL_MAGIC_WALL_EMPTYING ||
3396           e == EL_BD_MAGIC_WALL_FILLING ||
3397           e == EL_BD_MAGIC_WALL_EMPTYING)
3398         element_info[e].move_stepsize *= 2;
3399     }
3400   }
3401
3402   // ---------- initialize collect score --------------------------------------
3403
3404   // initialize collect score values for custom elements from initial value
3405   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3406     if (IS_CUSTOM_ELEMENT(i))
3407       element_info[i].collect_score = element_info[i].collect_score_initial;
3408
3409   // ---------- initialize collect count --------------------------------------
3410
3411   // initialize collect count values for non-custom elements
3412   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3413     if (!IS_CUSTOM_ELEMENT(i))
3414       element_info[i].collect_count_initial = 0;
3415
3416   // add collect count values for all elements from pre-defined list
3417   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3418     element_info[collect_count_list[i].element].collect_count_initial =
3419       collect_count_list[i].count;
3420
3421   // ---------- initialize access direction -----------------------------------
3422
3423   // initialize access direction values to default (access from every side)
3424   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3425     if (!IS_CUSTOM_ELEMENT(i))
3426       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3427
3428   // set access direction value for certain elements from pre-defined list
3429   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3430     element_info[access_direction_list[i].element].access_direction =
3431       access_direction_list[i].direction;
3432
3433   // ---------- initialize explosion content ----------------------------------
3434   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3435   {
3436     if (IS_CUSTOM_ELEMENT(i))
3437       continue;
3438
3439     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3440     {
3441       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3442
3443       element_info[i].content.e[x][y] =
3444         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3445          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3446          i == EL_PLAYER_3 ? EL_EMERALD :
3447          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3448          i == EL_MOLE ? EL_EMERALD_RED :
3449          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3450          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3451          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3452          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3453          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3454          i == EL_WALL_EMERALD ? EL_EMERALD :
3455          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3456          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3457          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3458          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3459          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3460          i == EL_WALL_PEARL ? EL_PEARL :
3461          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3462          EL_EMPTY);
3463     }
3464   }
3465
3466   // ---------- initialize recursion detection --------------------------------
3467   recursion_loop_depth = 0;
3468   recursion_loop_detected = FALSE;
3469   recursion_loop_element = EL_UNDEFINED;
3470
3471   // ---------- initialize graphics engine ------------------------------------
3472   game.scroll_delay_value =
3473     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3474      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3475      !setup.forced_scroll_delay           ? 0 :
3476      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3477   game.scroll_delay_value =
3478     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3479
3480   // ---------- initialize game engine snapshots ------------------------------
3481   for (i = 0; i < MAX_PLAYERS; i++)
3482     game.snapshot.last_action[i] = 0;
3483   game.snapshot.changed_action = FALSE;
3484   game.snapshot.collected_item = FALSE;
3485   game.snapshot.mode =
3486     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3487      SNAPSHOT_MODE_EVERY_STEP :
3488      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3489      SNAPSHOT_MODE_EVERY_MOVE :
3490      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3491      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3492   game.snapshot.save_snapshot = FALSE;
3493
3494   // ---------- initialize level time for Supaplex engine ---------------------
3495   // Supaplex levels with time limit currently unsupported -- should be added
3496   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3497     level.time = 0;
3498
3499   // ---------- initialize flags for handling game actions --------------------
3500
3501   // set flags for game actions to default values
3502   game.use_key_actions = TRUE;
3503   game.use_mouse_actions = FALSE;
3504
3505   // when using Mirror Magic game engine, handle mouse events only
3506   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3507   {
3508     game.use_key_actions = FALSE;
3509     game.use_mouse_actions = TRUE;
3510   }
3511
3512   // check for custom elements with mouse click events
3513   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3514   {
3515     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3516     {
3517       int element = EL_CUSTOM_START + i;
3518
3519       if (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3520           HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3521           HAS_ANY_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3522           HAS_ANY_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3523         game.use_mouse_actions = TRUE;
3524     }
3525   }
3526 }
3527
3528 static int get_num_special_action(int element, int action_first,
3529                                   int action_last)
3530 {
3531   int num_special_action = 0;
3532   int i, j;
3533
3534   for (i = action_first; i <= action_last; i++)
3535   {
3536     boolean found = FALSE;
3537
3538     for (j = 0; j < NUM_DIRECTIONS; j++)
3539       if (el_act_dir2img(element, i, j) !=
3540           el_act_dir2img(element, ACTION_DEFAULT, j))
3541         found = TRUE;
3542
3543     if (found)
3544       num_special_action++;
3545     else
3546       break;
3547   }
3548
3549   return num_special_action;
3550 }
3551
3552
3553 // ============================================================================
3554 // InitGame()
3555 // ----------------------------------------------------------------------------
3556 // initialize and start new game
3557 // ============================================================================
3558
3559 #if DEBUG_INIT_PLAYER
3560 static void DebugPrintPlayerStatus(char *message)
3561 {
3562   int i;
3563
3564   if (!options.debug)
3565     return;
3566
3567   Debug("game:init:player", "%s:", message);
3568
3569   for (i = 0; i < MAX_PLAYERS; i++)
3570   {
3571     struct PlayerInfo *player = &stored_player[i];
3572
3573     Debug("game:init:player",
3574           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3575           i + 1,
3576           player->present,
3577           player->connected,
3578           player->connected_locally,
3579           player->connected_network,
3580           player->active,
3581           (local_player == player ? " (local player)" : ""));
3582   }
3583 }
3584 #endif
3585
3586 void InitGame(void)
3587 {
3588   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3589   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3590   int fade_mask = REDRAW_FIELD;
3591   boolean restarting = (game_status == GAME_MODE_PLAYING);
3592   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3593   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3594   int initial_move_dir = MV_DOWN;
3595   int i, j, x, y;
3596
3597   // required here to update video display before fading (FIX THIS)
3598   DrawMaskedBorder(REDRAW_DOOR_2);
3599
3600   if (!game.restart_level)
3601     CloseDoor(DOOR_CLOSE_1);
3602
3603   if (restarting)
3604   {
3605     // force fading out global animations displayed during game play
3606     SetGameStatus(GAME_MODE_PSEUDO_RESTARTING);
3607   }
3608   else
3609   {
3610     SetGameStatus(GAME_MODE_PLAYING);
3611   }
3612
3613   if (level_editor_test_game)
3614     FadeSkipNextFadeOut();
3615   else
3616     FadeSetEnterScreen();
3617
3618   if (CheckFadeAll())
3619     fade_mask = REDRAW_ALL;
3620
3621   FadeLevelSoundsAndMusic();
3622
3623   ExpireSoundLoops(TRUE);
3624
3625   FadeOut(fade_mask);
3626
3627   if (restarting)
3628   {
3629     // force restarting global animations displayed during game play
3630     RestartGlobalAnimsByStatus(GAME_MODE_PSEUDO_RESTARTING);
3631
3632     // this is required for "transforming" fade modes like cross-fading
3633     // (else global animations will be stopped, but not restarted here)
3634     SetAnimStatusBeforeFading(GAME_MODE_PSEUDO_RESTARTING);
3635
3636     SetGameStatus(GAME_MODE_PLAYING);
3637   }
3638
3639   if (level_editor_test_game)
3640     FadeSkipNextFadeIn();
3641
3642   // needed if different viewport properties defined for playing
3643   ChangeViewportPropertiesIfNeeded();
3644
3645   ClearField();
3646
3647   DrawCompleteVideoDisplay();
3648
3649   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3650
3651   InitGameEngine();
3652   InitGameControlValues();
3653
3654   if (tape.recording)
3655   {
3656     // initialize tape actions from game when recording tape
3657     tape.use_key_actions   = game.use_key_actions;
3658     tape.use_mouse_actions = game.use_mouse_actions;
3659
3660     // initialize visible playfield size when recording tape (for team mode)
3661     tape.scr_fieldx = SCR_FIELDX;
3662     tape.scr_fieldy = SCR_FIELDY;
3663   }
3664
3665   // don't play tapes over network
3666   network_playing = (network.enabled && !tape.playing);
3667
3668   for (i = 0; i < MAX_PLAYERS; i++)
3669   {
3670     struct PlayerInfo *player = &stored_player[i];
3671
3672     player->index_nr = i;
3673     player->index_bit = (1 << i);
3674     player->element_nr = EL_PLAYER_1 + i;
3675
3676     player->present = FALSE;
3677     player->active = FALSE;
3678     player->mapped = FALSE;
3679
3680     player->killed = FALSE;
3681     player->reanimated = FALSE;
3682     player->buried = FALSE;
3683
3684     player->action = 0;
3685     player->effective_action = 0;
3686     player->programmed_action = 0;
3687     player->snap_action = 0;
3688
3689     player->mouse_action.lx = 0;
3690     player->mouse_action.ly = 0;
3691     player->mouse_action.button = 0;
3692     player->mouse_action.button_hint = 0;
3693
3694     player->effective_mouse_action.lx = 0;
3695     player->effective_mouse_action.ly = 0;
3696     player->effective_mouse_action.button = 0;
3697     player->effective_mouse_action.button_hint = 0;
3698
3699     for (j = 0; j < MAX_NUM_KEYS; j++)
3700       player->key[j] = FALSE;
3701
3702     player->num_white_keys = 0;
3703
3704     player->dynabomb_count = 0;
3705     player->dynabomb_size = 1;
3706     player->dynabombs_left = 0;
3707     player->dynabomb_xl = FALSE;
3708
3709     player->MovDir = initial_move_dir;
3710     player->MovPos = 0;
3711     player->GfxPos = 0;
3712     player->GfxDir = initial_move_dir;
3713     player->GfxAction = ACTION_DEFAULT;
3714     player->Frame = 0;
3715     player->StepFrame = 0;
3716
3717     player->initial_element = player->element_nr;
3718     player->artwork_element =
3719       (level.use_artwork_element[i] ? level.artwork_element[i] :
3720        player->element_nr);
3721     player->use_murphy = FALSE;
3722
3723     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3724     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3725
3726     player->gravity = level.initial_player_gravity[i];
3727
3728     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3729
3730     player->actual_frame_counter.count = 0;
3731     player->actual_frame_counter.value = 1;
3732
3733     player->step_counter = 0;
3734
3735     player->last_move_dir = initial_move_dir;
3736
3737     player->is_active = FALSE;
3738
3739     player->is_waiting = FALSE;
3740     player->is_moving = FALSE;
3741     player->is_auto_moving = FALSE;
3742     player->is_digging = FALSE;
3743     player->is_snapping = FALSE;
3744     player->is_collecting = FALSE;
3745     player->is_pushing = FALSE;
3746     player->is_switching = FALSE;
3747     player->is_dropping = FALSE;
3748     player->is_dropping_pressed = FALSE;
3749
3750     player->is_bored = FALSE;
3751     player->is_sleeping = FALSE;
3752
3753     player->was_waiting = TRUE;
3754     player->was_moving = FALSE;
3755     player->was_snapping = FALSE;
3756     player->was_dropping = FALSE;
3757
3758     player->force_dropping = FALSE;
3759
3760     player->frame_counter_bored = -1;
3761     player->frame_counter_sleeping = -1;
3762
3763     player->anim_delay_counter = 0;
3764     player->post_delay_counter = 0;
3765
3766     player->dir_waiting = initial_move_dir;
3767     player->action_waiting = ACTION_DEFAULT;
3768     player->last_action_waiting = ACTION_DEFAULT;
3769     player->special_action_bored = ACTION_DEFAULT;
3770     player->special_action_sleeping = ACTION_DEFAULT;
3771
3772     player->switch_x = -1;
3773     player->switch_y = -1;
3774
3775     player->drop_x = -1;
3776     player->drop_y = -1;
3777
3778     player->show_envelope = 0;
3779
3780     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3781
3782     player->push_delay       = -1;      // initialized when pushing starts
3783     player->push_delay_value = game.initial_push_delay_value;
3784
3785     player->drop_delay = 0;
3786     player->drop_pressed_delay = 0;
3787
3788     player->last_jx = -1;
3789     player->last_jy = -1;
3790     player->jx = -1;
3791     player->jy = -1;
3792
3793     player->shield_normal_time_left = 0;
3794     player->shield_deadly_time_left = 0;
3795
3796     player->last_removed_element = EL_UNDEFINED;
3797
3798     player->inventory_infinite_element = EL_UNDEFINED;
3799     player->inventory_size = 0;
3800
3801     if (level.use_initial_inventory[i])
3802     {
3803       for (j = 0; j < level.initial_inventory_size[i]; j++)
3804       {
3805         int element = level.initial_inventory_content[i][j];
3806         int collect_count = element_info[element].collect_count_initial;
3807         int k;
3808
3809         if (!IS_CUSTOM_ELEMENT(element))
3810           collect_count = 1;
3811
3812         if (collect_count == 0)
3813           player->inventory_infinite_element = element;
3814         else
3815           for (k = 0; k < collect_count; k++)
3816             if (player->inventory_size < MAX_INVENTORY_SIZE)
3817               player->inventory_element[player->inventory_size++] = element;
3818       }
3819     }
3820
3821     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3822     SnapField(player, 0, 0);
3823
3824     map_player_action[i] = i;
3825   }
3826
3827   network_player_action_received = FALSE;
3828
3829   // initial null action
3830   if (network_playing)
3831     SendToServer_MovePlayer(MV_NONE);
3832
3833   FrameCounter = 0;
3834   TimeFrames = 0;
3835   TimePlayed = 0;
3836   TimeLeft = level.time;
3837   TapeTime = 0;
3838
3839   ScreenMovDir = MV_NONE;
3840   ScreenMovPos = 0;
3841   ScreenGfxPos = 0;
3842
3843   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3844
3845   game.robot_wheel_x = -1;
3846   game.robot_wheel_y = -1;
3847
3848   game.exit_x = -1;
3849   game.exit_y = -1;
3850
3851   game.all_players_gone = FALSE;
3852
3853   game.LevelSolved = FALSE;
3854   game.GameOver = FALSE;
3855
3856   game.GamePlayed = !tape.playing;
3857
3858   game.LevelSolved_GameWon = FALSE;
3859   game.LevelSolved_GameEnd = FALSE;
3860   game.LevelSolved_SaveTape = FALSE;
3861   game.LevelSolved_SaveScore = FALSE;
3862
3863   game.LevelSolved_CountingTime = 0;
3864   game.LevelSolved_CountingScore = 0;
3865   game.LevelSolved_CountingHealth = 0;
3866
3867   game.panel.active = TRUE;
3868
3869   game.no_level_time_limit = (level.time == 0);
3870   game.time_limit = (leveldir_current->time_limit && setup.time_limit);
3871
3872   game.yamyam_content_nr = 0;
3873   game.robot_wheel_active = FALSE;
3874   game.magic_wall_active = FALSE;
3875   game.magic_wall_time_left = 0;
3876   game.light_time_left = 0;
3877   game.timegate_time_left = 0;
3878   game.switchgate_pos = 0;
3879   game.wind_direction = level.wind_direction_initial;
3880
3881   game.time_final = 0;
3882   game.score_time_final = 0;
3883
3884   game.score = 0;
3885   game.score_final = 0;
3886
3887   game.health = MAX_HEALTH;
3888   game.health_final = MAX_HEALTH;
3889
3890   game.gems_still_needed = level.gems_needed;
3891   game.sokoban_fields_still_needed = 0;
3892   game.sokoban_objects_still_needed = 0;
3893   game.lights_still_needed = 0;
3894   game.players_still_needed = 0;
3895   game.friends_still_needed = 0;
3896
3897   game.lenses_time_left = 0;
3898   game.magnify_time_left = 0;
3899
3900   game.ball_active = level.ball_active_initial;
3901   game.ball_content_nr = 0;
3902
3903   game.explosions_delayed = TRUE;
3904
3905   game.envelope_active = FALSE;
3906
3907   // special case: set custom artwork setting to initial value
3908   game.use_masked_elements = game.use_masked_elements_initial;
3909
3910   for (i = 0; i < NUM_BELTS; i++)
3911   {
3912     game.belt_dir[i] = MV_NONE;
3913     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3914   }
3915
3916   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3917     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3918
3919 #if DEBUG_INIT_PLAYER
3920   DebugPrintPlayerStatus("Player status at level initialization");
3921 #endif
3922
3923   SCAN_PLAYFIELD(x, y)
3924   {
3925     Tile[x][y] = Last[x][y] = level.field[x][y];
3926     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3927     ChangeDelay[x][y] = 0;
3928     ChangePage[x][y] = -1;
3929     CustomValue[x][y] = 0;              // initialized in InitField()
3930     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3931     AmoebaNr[x][y] = 0;
3932     WasJustMoving[x][y] = 0;
3933     WasJustFalling[x][y] = 0;
3934     CheckCollision[x][y] = 0;
3935     CheckImpact[x][y] = 0;
3936     Stop[x][y] = FALSE;
3937     Pushed[x][y] = FALSE;
3938
3939     ChangeCount[x][y] = 0;
3940     ChangeEvent[x][y] = -1;
3941
3942     ExplodePhase[x][y] = 0;
3943     ExplodeDelay[x][y] = 0;
3944     ExplodeField[x][y] = EX_TYPE_NONE;
3945
3946     RunnerVisit[x][y] = 0;
3947     PlayerVisit[x][y] = 0;
3948
3949     GfxFrame[x][y] = 0;
3950     GfxRandom[x][y] = INIT_GFX_RANDOM();
3951     GfxRandomStatic[x][y] = INIT_GFX_RANDOM();
3952     GfxElement[x][y] = EL_UNDEFINED;
3953     GfxElementEmpty[x][y] = EL_EMPTY;
3954     GfxAction[x][y] = ACTION_DEFAULT;
3955     GfxDir[x][y] = MV_NONE;
3956     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3957   }
3958
3959   SCAN_PLAYFIELD(x, y)
3960   {
3961     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3962       emulate_bd = FALSE;
3963     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3964       emulate_sp = FALSE;
3965
3966     InitField(x, y, TRUE);
3967
3968     ResetGfxAnimation(x, y);
3969   }
3970
3971   InitBeltMovement();
3972
3973   for (i = 0; i < MAX_PLAYERS; i++)
3974   {
3975     struct PlayerInfo *player = &stored_player[i];
3976
3977     // set number of special actions for bored and sleeping animation
3978     player->num_special_action_bored =
3979       get_num_special_action(player->artwork_element,
3980                              ACTION_BORING_1, ACTION_BORING_LAST);
3981     player->num_special_action_sleeping =
3982       get_num_special_action(player->artwork_element,
3983                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3984   }
3985
3986   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3987                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3988
3989   // initialize type of slippery elements
3990   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3991   {
3992     if (!IS_CUSTOM_ELEMENT(i))
3993     {
3994       // default: elements slip down either to the left or right randomly
3995       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3996
3997       // SP style elements prefer to slip down on the left side
3998       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3999         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4000
4001       // BD style elements prefer to slip down on the left side
4002       if (game.emulation == EMU_BOULDERDASH)
4003         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4004     }
4005   }
4006
4007   // initialize explosion and ignition delay
4008   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4009   {
4010     if (!IS_CUSTOM_ELEMENT(i))
4011     {
4012       int num_phase = 8;
4013       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4014                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4015                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
4016       int last_phase = (num_phase + 1) * delay;
4017       int half_phase = (num_phase / 2) * delay;
4018
4019       element_info[i].explosion_delay = last_phase - 1;
4020       element_info[i].ignition_delay = half_phase;
4021
4022       if (i == EL_BLACK_ORB)
4023         element_info[i].ignition_delay = 1;
4024     }
4025   }
4026
4027   // correct non-moving belts to start moving left
4028   for (i = 0; i < NUM_BELTS; i++)
4029     if (game.belt_dir[i] == MV_NONE)
4030       game.belt_dir_nr[i] = 3;          // not moving, next moving left
4031
4032 #if USE_NEW_PLAYER_ASSIGNMENTS
4033   // use preferred player also in local single-player mode
4034   if (!network.enabled && !game.team_mode)
4035   {
4036     int new_index_nr = setup.network_player_nr;
4037
4038     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
4039     {
4040       for (i = 0; i < MAX_PLAYERS; i++)
4041         stored_player[i].connected_locally = FALSE;
4042
4043       stored_player[new_index_nr].connected_locally = TRUE;
4044     }
4045   }
4046
4047   for (i = 0; i < MAX_PLAYERS; i++)
4048   {
4049     stored_player[i].connected = FALSE;
4050
4051     // in network game mode, the local player might not be the first player
4052     if (stored_player[i].connected_locally)
4053       local_player = &stored_player[i];
4054   }
4055
4056   if (!network.enabled)
4057     local_player->connected = TRUE;
4058
4059   if (tape.playing)
4060   {
4061     for (i = 0; i < MAX_PLAYERS; i++)
4062       stored_player[i].connected = tape.player_participates[i];
4063   }
4064   else if (network.enabled)
4065   {
4066     // add team mode players connected over the network (needed for correct
4067     // assignment of player figures from level to locally playing players)
4068
4069     for (i = 0; i < MAX_PLAYERS; i++)
4070       if (stored_player[i].connected_network)
4071         stored_player[i].connected = TRUE;
4072   }
4073   else if (game.team_mode)
4074   {
4075     // try to guess locally connected team mode players (needed for correct
4076     // assignment of player figures from level to locally playing players)
4077
4078     for (i = 0; i < MAX_PLAYERS; i++)
4079       if (setup.input[i].use_joystick ||
4080           setup.input[i].key.left != KSYM_UNDEFINED)
4081         stored_player[i].connected = TRUE;
4082   }
4083
4084 #if DEBUG_INIT_PLAYER
4085   DebugPrintPlayerStatus("Player status after level initialization");
4086 #endif
4087
4088 #if DEBUG_INIT_PLAYER
4089   Debug("game:init:player", "Reassigning players ...");
4090 #endif
4091
4092   // check if any connected player was not found in playfield
4093   for (i = 0; i < MAX_PLAYERS; i++)
4094   {
4095     struct PlayerInfo *player = &stored_player[i];
4096
4097     if (player->connected && !player->present)
4098     {
4099       struct PlayerInfo *field_player = NULL;
4100
4101 #if DEBUG_INIT_PLAYER
4102       Debug("game:init:player",
4103             "- looking for field player for player %d ...", i + 1);
4104 #endif
4105
4106       // assign first free player found that is present in the playfield
4107
4108       // first try: look for unmapped playfield player that is not connected
4109       for (j = 0; j < MAX_PLAYERS; j++)
4110         if (field_player == NULL &&
4111             stored_player[j].present &&
4112             !stored_player[j].mapped &&
4113             !stored_player[j].connected)
4114           field_player = &stored_player[j];
4115
4116       // second try: look for *any* unmapped playfield player
4117       for (j = 0; j < MAX_PLAYERS; j++)
4118         if (field_player == NULL &&
4119             stored_player[j].present &&
4120             !stored_player[j].mapped)
4121           field_player = &stored_player[j];
4122
4123       if (field_player != NULL)
4124       {
4125         int jx = field_player->jx, jy = field_player->jy;
4126
4127 #if DEBUG_INIT_PLAYER
4128         Debug("game:init:player", "- found player %d",
4129               field_player->index_nr + 1);
4130 #endif
4131
4132         player->present = FALSE;
4133         player->active = FALSE;
4134
4135         field_player->present = TRUE;
4136         field_player->active = TRUE;
4137
4138         /*
4139         player->initial_element = field_player->initial_element;
4140         player->artwork_element = field_player->artwork_element;
4141
4142         player->block_last_field       = field_player->block_last_field;
4143         player->block_delay_adjustment = field_player->block_delay_adjustment;
4144         */
4145
4146         StorePlayer[jx][jy] = field_player->element_nr;
4147
4148         field_player->jx = field_player->last_jx = jx;
4149         field_player->jy = field_player->last_jy = jy;
4150
4151         if (local_player == player)
4152           local_player = field_player;
4153
4154         map_player_action[field_player->index_nr] = i;
4155
4156         field_player->mapped = TRUE;
4157
4158 #if DEBUG_INIT_PLAYER
4159         Debug("game:init:player", "- map_player_action[%d] == %d",
4160               field_player->index_nr + 1, i + 1);
4161 #endif
4162       }
4163     }
4164
4165     if (player->connected && player->present)
4166       player->mapped = TRUE;
4167   }
4168
4169 #if DEBUG_INIT_PLAYER
4170   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4171 #endif
4172
4173 #else
4174
4175   // check if any connected player was not found in playfield
4176   for (i = 0; i < MAX_PLAYERS; i++)
4177   {
4178     struct PlayerInfo *player = &stored_player[i];
4179
4180     if (player->connected && !player->present)
4181     {
4182       for (j = 0; j < MAX_PLAYERS; j++)
4183       {
4184         struct PlayerInfo *field_player = &stored_player[j];
4185         int jx = field_player->jx, jy = field_player->jy;
4186
4187         // assign first free player found that is present in the playfield
4188         if (field_player->present && !field_player->connected)
4189         {
4190           player->present = TRUE;
4191           player->active = TRUE;
4192
4193           field_player->present = FALSE;
4194           field_player->active = FALSE;
4195
4196           player->initial_element = field_player->initial_element;
4197           player->artwork_element = field_player->artwork_element;
4198
4199           player->block_last_field       = field_player->block_last_field;
4200           player->block_delay_adjustment = field_player->block_delay_adjustment;
4201
4202           StorePlayer[jx][jy] = player->element_nr;
4203
4204           player->jx = player->last_jx = jx;
4205           player->jy = player->last_jy = jy;
4206
4207           break;
4208         }
4209       }
4210     }
4211   }
4212 #endif
4213
4214 #if 0
4215   Debug("game:init:player", "local_player->present == %d",
4216         local_player->present);
4217 #endif
4218
4219   // set focus to local player for network games, else to all players
4220   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4221   game.centered_player_nr_next = game.centered_player_nr;
4222   game.set_centered_player = FALSE;
4223   game.set_centered_player_wrap = FALSE;
4224
4225   if (network_playing && tape.recording)
4226   {
4227     // store client dependent player focus when recording network games
4228     tape.centered_player_nr_next = game.centered_player_nr_next;
4229     tape.set_centered_player = TRUE;
4230   }
4231
4232   if (tape.playing)
4233   {
4234     // when playing a tape, eliminate all players who do not participate
4235
4236 #if USE_NEW_PLAYER_ASSIGNMENTS
4237
4238     if (!game.team_mode)
4239     {
4240       for (i = 0; i < MAX_PLAYERS; i++)
4241       {
4242         if (stored_player[i].active &&
4243             !tape.player_participates[map_player_action[i]])
4244         {
4245           struct PlayerInfo *player = &stored_player[i];
4246           int jx = player->jx, jy = player->jy;
4247
4248 #if DEBUG_INIT_PLAYER
4249           Debug("game:init:player", "Removing player %d at (%d, %d)",
4250                 i + 1, jx, jy);
4251 #endif
4252
4253           player->active = FALSE;
4254           StorePlayer[jx][jy] = 0;
4255           Tile[jx][jy] = EL_EMPTY;
4256         }
4257       }
4258     }
4259
4260 #else
4261
4262     for (i = 0; i < MAX_PLAYERS; i++)
4263     {
4264       if (stored_player[i].active &&
4265           !tape.player_participates[i])
4266       {
4267         struct PlayerInfo *player = &stored_player[i];
4268         int jx = player->jx, jy = player->jy;
4269
4270         player->active = FALSE;
4271         StorePlayer[jx][jy] = 0;
4272         Tile[jx][jy] = EL_EMPTY;
4273       }
4274     }
4275 #endif
4276   }
4277   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4278   {
4279     // when in single player mode, eliminate all but the local player
4280
4281     for (i = 0; i < MAX_PLAYERS; i++)
4282     {
4283       struct PlayerInfo *player = &stored_player[i];
4284
4285       if (player->active && player != local_player)
4286       {
4287         int jx = player->jx, jy = player->jy;
4288
4289         player->active = FALSE;
4290         player->present = FALSE;
4291
4292         StorePlayer[jx][jy] = 0;
4293         Tile[jx][jy] = EL_EMPTY;
4294       }
4295     }
4296   }
4297
4298   for (i = 0; i < MAX_PLAYERS; i++)
4299     if (stored_player[i].active)
4300       game.players_still_needed++;
4301
4302   if (level.solved_by_one_player)
4303     game.players_still_needed = 1;
4304
4305   // when recording the game, store which players take part in the game
4306   if (tape.recording)
4307   {
4308 #if USE_NEW_PLAYER_ASSIGNMENTS
4309     for (i = 0; i < MAX_PLAYERS; i++)
4310       if (stored_player[i].connected)
4311         tape.player_participates[i] = TRUE;
4312 #else
4313     for (i = 0; i < MAX_PLAYERS; i++)
4314       if (stored_player[i].active)
4315         tape.player_participates[i] = TRUE;
4316 #endif
4317   }
4318
4319 #if DEBUG_INIT_PLAYER
4320   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4321 #endif
4322
4323   if (BorderElement == EL_EMPTY)
4324   {
4325     SBX_Left = 0;
4326     SBX_Right = lev_fieldx - SCR_FIELDX;
4327     SBY_Upper = 0;
4328     SBY_Lower = lev_fieldy - SCR_FIELDY;
4329   }
4330   else
4331   {
4332     SBX_Left = -1;
4333     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4334     SBY_Upper = -1;
4335     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4336   }
4337
4338   if (full_lev_fieldx <= SCR_FIELDX)
4339     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4340   if (full_lev_fieldy <= SCR_FIELDY)
4341     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4342
4343   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4344     SBX_Left--;
4345   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4346     SBY_Upper--;
4347
4348   // if local player not found, look for custom element that might create
4349   // the player (make some assumptions about the right custom element)
4350   if (!local_player->present)
4351   {
4352     int start_x = 0, start_y = 0;
4353     int found_rating = 0;
4354     int found_element = EL_UNDEFINED;
4355     int player_nr = local_player->index_nr;
4356
4357     SCAN_PLAYFIELD(x, y)
4358     {
4359       int element = Tile[x][y];
4360       int content;
4361       int xx, yy;
4362       boolean is_player;
4363
4364       if (level.use_start_element[player_nr] &&
4365           level.start_element[player_nr] == element &&
4366           found_rating < 4)
4367       {
4368         start_x = x;
4369         start_y = y;
4370
4371         found_rating = 4;
4372         found_element = element;
4373       }
4374
4375       if (!IS_CUSTOM_ELEMENT(element))
4376         continue;
4377
4378       if (CAN_CHANGE(element))
4379       {
4380         for (i = 0; i < element_info[element].num_change_pages; i++)
4381         {
4382           // check for player created from custom element as single target
4383           content = element_info[element].change_page[i].target_element;
4384           is_player = IS_PLAYER_ELEMENT(content);
4385
4386           if (is_player && (found_rating < 3 ||
4387                             (found_rating == 3 && element < found_element)))
4388           {
4389             start_x = x;
4390             start_y = y;
4391
4392             found_rating = 3;
4393             found_element = element;
4394           }
4395         }
4396       }
4397
4398       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4399       {
4400         // check for player created from custom element as explosion content
4401         content = element_info[element].content.e[xx][yy];
4402         is_player = IS_PLAYER_ELEMENT(content);
4403
4404         if (is_player && (found_rating < 2 ||
4405                           (found_rating == 2 && element < found_element)))
4406         {
4407           start_x = x + xx - 1;
4408           start_y = y + yy - 1;
4409
4410           found_rating = 2;
4411           found_element = element;
4412         }
4413
4414         if (!CAN_CHANGE(element))
4415           continue;
4416
4417         for (i = 0; i < element_info[element].num_change_pages; i++)
4418         {
4419           // check for player created from custom element as extended target
4420           content =
4421             element_info[element].change_page[i].target_content.e[xx][yy];
4422
4423           is_player = IS_PLAYER_ELEMENT(content);
4424
4425           if (is_player && (found_rating < 1 ||
4426                             (found_rating == 1 && element < found_element)))
4427           {
4428             start_x = x + xx - 1;
4429             start_y = y + yy - 1;
4430
4431             found_rating = 1;
4432             found_element = element;
4433           }
4434         }
4435       }
4436     }
4437
4438     scroll_x = SCROLL_POSITION_X(start_x);
4439     scroll_y = SCROLL_POSITION_Y(start_y);
4440   }
4441   else
4442   {
4443     scroll_x = SCROLL_POSITION_X(local_player->jx);
4444     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4445   }
4446
4447   // !!! FIX THIS (START) !!!
4448   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4449   {
4450     InitGameEngine_EM();
4451   }
4452   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4453   {
4454     InitGameEngine_SP();
4455   }
4456   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4457   {
4458     InitGameEngine_MM();
4459   }
4460   else
4461   {
4462     DrawLevel(REDRAW_FIELD);
4463     DrawAllPlayers();
4464
4465     // after drawing the level, correct some elements
4466     if (game.timegate_time_left == 0)
4467       CloseAllOpenTimegates();
4468   }
4469
4470   // blit playfield from scroll buffer to normal back buffer for fading in
4471   BlitScreenToBitmap(backbuffer);
4472   // !!! FIX THIS (END) !!!
4473
4474   DrawMaskedBorder(fade_mask);
4475
4476   FadeIn(fade_mask);
4477
4478 #if 1
4479   // full screen redraw is required at this point in the following cases:
4480   // - special editor door undrawn when game was started from level editor
4481   // - drawing area (playfield) was changed and has to be removed completely
4482   redraw_mask = REDRAW_ALL;
4483   BackToFront();
4484 #endif
4485
4486   if (!game.restart_level)
4487   {
4488     // copy default game door content to main double buffer
4489
4490     // !!! CHECK AGAIN !!!
4491     SetPanelBackground();
4492     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4493     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4494   }
4495
4496   SetPanelBackground();
4497   SetDrawBackgroundMask(REDRAW_DOOR_1);
4498
4499   UpdateAndDisplayGameControlValues();
4500
4501   if (!game.restart_level)
4502   {
4503     UnmapGameButtons();
4504     UnmapTapeButtons();
4505
4506     FreeGameButtons();
4507     CreateGameButtons();
4508
4509     MapGameButtons();
4510     MapTapeButtons();
4511
4512     // copy actual game door content to door double buffer for OpenDoor()
4513     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4514
4515     OpenDoor(DOOR_OPEN_ALL);
4516
4517     KeyboardAutoRepeatOffUnlessAutoplay();
4518
4519 #if DEBUG_INIT_PLAYER
4520     DebugPrintPlayerStatus("Player status (final)");
4521 #endif
4522   }
4523
4524   UnmapAllGadgets();
4525
4526   MapGameButtons();
4527   MapTapeButtons();
4528
4529   if (!game.restart_level && !tape.playing)
4530   {
4531     LevelStats_incPlayed(level_nr);
4532
4533     SaveLevelSetup_SeriesInfo();
4534   }
4535
4536   game.restart_level = FALSE;
4537
4538   game.request_active = FALSE;
4539   game.request_active_or_moving = FALSE;
4540
4541   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4542     InitGameActions_MM();
4543
4544   SaveEngineSnapshotToListInitial();
4545
4546   if (!game.restart_level)
4547   {
4548     PlaySound(SND_GAME_STARTING);
4549
4550     if (setup.sound_music)
4551       PlayLevelMusic();
4552   }
4553
4554   SetPlayfieldMouseCursorEnabled(!game.use_mouse_actions);
4555 }
4556
4557 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4558                         int actual_player_x, int actual_player_y)
4559 {
4560   // this is used for non-R'n'D game engines to update certain engine values
4561
4562   // needed to determine if sounds are played within the visible screen area
4563   scroll_x = actual_scroll_x;
4564   scroll_y = actual_scroll_y;
4565
4566   // needed to get player position for "follow finger" playing input method
4567   local_player->jx = actual_player_x;
4568   local_player->jy = actual_player_y;
4569 }
4570
4571 void InitMovDir(int x, int y)
4572 {
4573   int i, element = Tile[x][y];
4574   static int xy[4][2] =
4575   {
4576     {  0, +1 },
4577     { +1,  0 },
4578     {  0, -1 },
4579     { -1,  0 }
4580   };
4581   static int direction[3][4] =
4582   {
4583     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4584     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4585     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4586   };
4587
4588   switch (element)
4589   {
4590     case EL_BUG_RIGHT:
4591     case EL_BUG_UP:
4592     case EL_BUG_LEFT:
4593     case EL_BUG_DOWN:
4594       Tile[x][y] = EL_BUG;
4595       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4596       break;
4597
4598     case EL_SPACESHIP_RIGHT:
4599     case EL_SPACESHIP_UP:
4600     case EL_SPACESHIP_LEFT:
4601     case EL_SPACESHIP_DOWN:
4602       Tile[x][y] = EL_SPACESHIP;
4603       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4604       break;
4605
4606     case EL_BD_BUTTERFLY_RIGHT:
4607     case EL_BD_BUTTERFLY_UP:
4608     case EL_BD_BUTTERFLY_LEFT:
4609     case EL_BD_BUTTERFLY_DOWN:
4610       Tile[x][y] = EL_BD_BUTTERFLY;
4611       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4612       break;
4613
4614     case EL_BD_FIREFLY_RIGHT:
4615     case EL_BD_FIREFLY_UP:
4616     case EL_BD_FIREFLY_LEFT:
4617     case EL_BD_FIREFLY_DOWN:
4618       Tile[x][y] = EL_BD_FIREFLY;
4619       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4620       break;
4621
4622     case EL_PACMAN_RIGHT:
4623     case EL_PACMAN_UP:
4624     case EL_PACMAN_LEFT:
4625     case EL_PACMAN_DOWN:
4626       Tile[x][y] = EL_PACMAN;
4627       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4628       break;
4629
4630     case EL_YAMYAM_LEFT:
4631     case EL_YAMYAM_RIGHT:
4632     case EL_YAMYAM_UP:
4633     case EL_YAMYAM_DOWN:
4634       Tile[x][y] = EL_YAMYAM;
4635       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4636       break;
4637
4638     case EL_SP_SNIKSNAK:
4639       MovDir[x][y] = MV_UP;
4640       break;
4641
4642     case EL_SP_ELECTRON:
4643       MovDir[x][y] = MV_LEFT;
4644       break;
4645
4646     case EL_MOLE_LEFT:
4647     case EL_MOLE_RIGHT:
4648     case EL_MOLE_UP:
4649     case EL_MOLE_DOWN:
4650       Tile[x][y] = EL_MOLE;
4651       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4652       break;
4653
4654     case EL_SPRING_LEFT:
4655     case EL_SPRING_RIGHT:
4656       Tile[x][y] = EL_SPRING;
4657       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4658       break;
4659
4660     default:
4661       if (IS_CUSTOM_ELEMENT(element))
4662       {
4663         struct ElementInfo *ei = &element_info[element];
4664         int move_direction_initial = ei->move_direction_initial;
4665         int move_pattern = ei->move_pattern;
4666
4667         if (move_direction_initial == MV_START_PREVIOUS)
4668         {
4669           if (MovDir[x][y] != MV_NONE)
4670             return;
4671
4672           move_direction_initial = MV_START_AUTOMATIC;
4673         }
4674
4675         if (move_direction_initial == MV_START_RANDOM)
4676           MovDir[x][y] = 1 << RND(4);
4677         else if (move_direction_initial & MV_ANY_DIRECTION)
4678           MovDir[x][y] = move_direction_initial;
4679         else if (move_pattern == MV_ALL_DIRECTIONS ||
4680                  move_pattern == MV_TURNING_LEFT ||
4681                  move_pattern == MV_TURNING_RIGHT ||
4682                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4683                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4684                  move_pattern == MV_TURNING_RANDOM)
4685           MovDir[x][y] = 1 << RND(4);
4686         else if (move_pattern == MV_HORIZONTAL)
4687           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4688         else if (move_pattern == MV_VERTICAL)
4689           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4690         else if (move_pattern & MV_ANY_DIRECTION)
4691           MovDir[x][y] = element_info[element].move_pattern;
4692         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4693                  move_pattern == MV_ALONG_RIGHT_SIDE)
4694         {
4695           // use random direction as default start direction
4696           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4697             MovDir[x][y] = 1 << RND(4);
4698
4699           for (i = 0; i < NUM_DIRECTIONS; i++)
4700           {
4701             int x1 = x + xy[i][0];
4702             int y1 = y + xy[i][1];
4703
4704             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4705             {
4706               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4707                 MovDir[x][y] = direction[0][i];
4708               else
4709                 MovDir[x][y] = direction[1][i];
4710
4711               break;
4712             }
4713           }
4714         }                
4715       }
4716       else
4717       {
4718         MovDir[x][y] = 1 << RND(4);
4719
4720         if (element != EL_BUG &&
4721             element != EL_SPACESHIP &&
4722             element != EL_BD_BUTTERFLY &&
4723             element != EL_BD_FIREFLY)
4724           break;
4725
4726         for (i = 0; i < NUM_DIRECTIONS; i++)
4727         {
4728           int x1 = x + xy[i][0];
4729           int y1 = y + xy[i][1];
4730
4731           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4732           {
4733             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4734             {
4735               MovDir[x][y] = direction[0][i];
4736               break;
4737             }
4738             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4739                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4740             {
4741               MovDir[x][y] = direction[1][i];
4742               break;
4743             }
4744           }
4745         }
4746       }
4747       break;
4748   }
4749
4750   GfxDir[x][y] = MovDir[x][y];
4751 }
4752
4753 void InitAmoebaNr(int x, int y)
4754 {
4755   int i;
4756   int group_nr = AmoebaNeighbourNr(x, y);
4757
4758   if (group_nr == 0)
4759   {
4760     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4761     {
4762       if (AmoebaCnt[i] == 0)
4763       {
4764         group_nr = i;
4765         break;
4766       }
4767     }
4768   }
4769
4770   AmoebaNr[x][y] = group_nr;
4771   AmoebaCnt[group_nr]++;
4772   AmoebaCnt2[group_nr]++;
4773 }
4774
4775 static void LevelSolved_SetFinalGameValues(void)
4776 {
4777   game.time_final = (game.no_level_time_limit ? TimePlayed : TimeLeft);
4778   game.score_time_final = (level.use_step_counter ? TimePlayed :
4779                            TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4780
4781   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4782                       game_em.lev->score :
4783                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4784                       game_mm.score :
4785                       game.score);
4786
4787   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4788                        MM_HEALTH(game_mm.laser_overload_value) :
4789                        game.health);
4790
4791   game.LevelSolved_CountingTime = game.time_final;
4792   game.LevelSolved_CountingScore = game.score_final;
4793   game.LevelSolved_CountingHealth = game.health_final;
4794 }
4795
4796 static void LevelSolved_DisplayFinalGameValues(int time, int score, int health)
4797 {
4798   game.LevelSolved_CountingTime = time;
4799   game.LevelSolved_CountingScore = score;
4800   game.LevelSolved_CountingHealth = health;
4801
4802   game_panel_controls[GAME_PANEL_TIME].value = time;
4803   game_panel_controls[GAME_PANEL_SCORE].value = score;
4804   game_panel_controls[GAME_PANEL_HEALTH].value = health;
4805
4806   DisplayGameControlValues();
4807 }
4808
4809 static void LevelSolved(void)
4810 {
4811   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4812       game.players_still_needed > 0)
4813     return;
4814
4815   game.LevelSolved = TRUE;
4816   game.GameOver = TRUE;
4817
4818   tape.solved = TRUE;
4819
4820   // needed here to display correct panel values while player walks into exit
4821   LevelSolved_SetFinalGameValues();
4822 }
4823
4824 void GameWon(void)
4825 {
4826   static int time_count_steps;
4827   static int time, time_final;
4828   static float score, score_final; // needed for time score < 10 for 10 seconds
4829   static int health, health_final;
4830   static int game_over_delay_1 = 0;
4831   static int game_over_delay_2 = 0;
4832   static int game_over_delay_3 = 0;
4833   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4834   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4835
4836   if (!game.LevelSolved_GameWon)
4837   {
4838     int i;
4839
4840     // do not start end game actions before the player stops moving (to exit)
4841     if (local_player->active && local_player->MovPos)
4842       return;
4843
4844     // calculate final game values after player finished walking into exit
4845     LevelSolved_SetFinalGameValues();
4846
4847     game.LevelSolved_GameWon = TRUE;
4848     game.LevelSolved_SaveTape = tape.recording;
4849     game.LevelSolved_SaveScore = !tape.playing;
4850
4851     if (!tape.playing)
4852     {
4853       LevelStats_incSolved(level_nr);
4854
4855       SaveLevelSetup_SeriesInfo();
4856     }
4857
4858     if (tape.auto_play)         // tape might already be stopped here
4859       tape.auto_play_level_solved = TRUE;
4860
4861     TapeStop();
4862
4863     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4864     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4865     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4866
4867     time = time_final = game.time_final;
4868     score = score_final = game.score_final;
4869     health = health_final = game.health_final;
4870
4871     // update game panel values before (delayed) counting of score (if any)
4872     LevelSolved_DisplayFinalGameValues(time, score, health);
4873
4874     // if level has time score defined, calculate new final game values
4875     if (time_score > 0)
4876     {
4877       int time_final_max = 999;
4878       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4879       int time_frames = 0;
4880       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4881       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4882
4883       if (TimeLeft > 0)
4884       {
4885         time_final = 0;
4886         time_frames = time_frames_left;
4887       }
4888       else if (game.no_level_time_limit && TimePlayed < time_final_max)
4889       {
4890         time_final = time_final_max;
4891         time_frames = time_frames_final_max - time_frames_played;
4892       }
4893
4894       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4895
4896       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4897
4898       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4899       {
4900         health_final = 0;
4901         score_final += health * time_score;
4902       }
4903
4904       game.score_final = score_final;
4905       game.health_final = health_final;
4906     }
4907
4908     // if not counting score after game, immediately update game panel values
4909     if (level_editor_test_game || !setup.count_score_after_game)
4910     {
4911       time = time_final;
4912       score = score_final;
4913
4914       LevelSolved_DisplayFinalGameValues(time, score, health);
4915     }
4916
4917     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4918     {
4919       // check if last player has left the level
4920       if (game.exit_x >= 0 &&
4921           game.exit_y >= 0)
4922       {
4923         int x = game.exit_x;
4924         int y = game.exit_y;
4925         int element = Tile[x][y];
4926
4927         // close exit door after last player
4928         if ((game.all_players_gone &&
4929              (element == EL_EXIT_OPEN ||
4930               element == EL_SP_EXIT_OPEN ||
4931               element == EL_STEEL_EXIT_OPEN)) ||
4932             element == EL_EM_EXIT_OPEN ||
4933             element == EL_EM_STEEL_EXIT_OPEN)
4934         {
4935
4936           Tile[x][y] =
4937             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4938              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4939              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4940              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4941              EL_EM_STEEL_EXIT_CLOSING);
4942
4943           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4944         }
4945
4946         // player disappears
4947         DrawLevelField(x, y);
4948       }
4949
4950       for (i = 0; i < MAX_PLAYERS; i++)
4951       {
4952         struct PlayerInfo *player = &stored_player[i];
4953
4954         if (player->present)
4955         {
4956           RemovePlayer(player);
4957
4958           // player disappears
4959           DrawLevelField(player->jx, player->jy);
4960         }
4961       }
4962     }
4963
4964     PlaySound(SND_GAME_WINNING);
4965   }
4966
4967   if (setup.count_score_after_game)
4968   {
4969     if (time != time_final)
4970     {
4971       if (game_over_delay_1 > 0)
4972       {
4973         game_over_delay_1--;
4974
4975         return;
4976       }
4977
4978       int time_to_go = ABS(time_final - time);
4979       int time_count_dir = (time < time_final ? +1 : -1);
4980
4981       if (time_to_go < time_count_steps)
4982         time_count_steps = 1;
4983
4984       time  += time_count_steps * time_count_dir;
4985       score += time_count_steps * time_score;
4986
4987       // set final score to correct rounding differences after counting score
4988       if (time == time_final)
4989         score = score_final;
4990
4991       LevelSolved_DisplayFinalGameValues(time, score, health);
4992
4993       if (time == time_final)
4994         StopSound(SND_GAME_LEVELTIME_BONUS);
4995       else if (setup.sound_loops)
4996         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4997       else
4998         PlaySound(SND_GAME_LEVELTIME_BONUS);
4999
5000       return;
5001     }
5002
5003     if (health != health_final)
5004     {
5005       if (game_over_delay_2 > 0)
5006       {
5007         game_over_delay_2--;
5008
5009         return;
5010       }
5011
5012       int health_count_dir = (health < health_final ? +1 : -1);
5013
5014       health += health_count_dir;
5015       score  += time_score;
5016
5017       LevelSolved_DisplayFinalGameValues(time, score, health);
5018
5019       if (health == health_final)
5020         StopSound(SND_GAME_LEVELTIME_BONUS);
5021       else if (setup.sound_loops)
5022         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5023       else
5024         PlaySound(SND_GAME_LEVELTIME_BONUS);
5025
5026       return;
5027     }
5028   }
5029
5030   game.panel.active = FALSE;
5031
5032   if (game_over_delay_3 > 0)
5033   {
5034     game_over_delay_3--;
5035
5036     return;
5037   }
5038
5039   GameEnd();
5040 }
5041
5042 void GameEnd(void)
5043 {
5044   // used instead of "level_nr" (needed for network games)
5045   int last_level_nr = levelset.level_nr;
5046   boolean tape_saved = FALSE;
5047
5048   game.LevelSolved_GameEnd = TRUE;
5049
5050   if (game.LevelSolved_SaveTape && !score_info_tape_play)
5051   {
5052     // make sure that request dialog to save tape does not open door again
5053     if (!global.use_envelope_request)
5054       CloseDoor(DOOR_CLOSE_1);
5055
5056     // ask to save tape
5057     tape_saved = SaveTapeChecked_LevelSolved(tape.level_nr);
5058
5059     // set unique basename for score tape (also saved in high score table)
5060     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
5061   }
5062
5063   // if no tape is to be saved, close both doors simultaneously
5064   CloseDoor(DOOR_CLOSE_ALL);
5065
5066   if (level_editor_test_game || score_info_tape_play)
5067   {
5068     SetGameStatus(GAME_MODE_MAIN);
5069
5070     DrawMainMenu();
5071
5072     return;
5073   }
5074
5075   if (!game.LevelSolved_SaveScore)
5076   {
5077     SetGameStatus(GAME_MODE_MAIN);
5078
5079     DrawMainMenu();
5080
5081     return;
5082   }
5083
5084   if (level_nr == leveldir_current->handicap_level)
5085   {
5086     leveldir_current->handicap_level++;
5087
5088     SaveLevelSetup_SeriesInfo();
5089   }
5090
5091   // save score and score tape before potentially erasing tape below
5092   NewHighScore(last_level_nr, tape_saved);
5093
5094   if (setup.increment_levels &&
5095       level_nr < leveldir_current->last_level &&
5096       !network_playing)
5097   {
5098     level_nr++;         // advance to next level
5099     TapeErase();        // start with empty tape
5100
5101     if (setup.auto_play_next_level)
5102     {
5103       scores.continue_playing = TRUE;
5104       scores.next_level_nr = level_nr;
5105
5106       LoadLevel(level_nr);
5107
5108       SaveLevelSetup_SeriesInfo();
5109     }
5110   }
5111
5112   if (scores.last_added >= 0 && setup.show_scores_after_game)
5113   {
5114     SetGameStatus(GAME_MODE_SCORES);
5115
5116     DrawHallOfFame(last_level_nr);
5117   }
5118   else if (scores.continue_playing)
5119   {
5120     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5121   }
5122   else
5123   {
5124     SetGameStatus(GAME_MODE_MAIN);
5125
5126     DrawMainMenu();
5127   }
5128 }
5129
5130 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5131                          boolean one_score_entry_per_name)
5132 {
5133   int i;
5134
5135   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5136     return -1;
5137
5138   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5139   {
5140     struct ScoreEntry *entry = &list->entry[i];
5141     boolean score_is_better = (new_entry->score >  entry->score);
5142     boolean score_is_equal  = (new_entry->score == entry->score);
5143     boolean time_is_better  = (new_entry->time  <  entry->time);
5144     boolean time_is_equal   = (new_entry->time  == entry->time);
5145     boolean better_by_score = (score_is_better ||
5146                                (score_is_equal && time_is_better));
5147     boolean better_by_time  = (time_is_better ||
5148                                (time_is_equal && score_is_better));
5149     boolean is_better = (level.rate_time_over_score ? better_by_time :
5150                          better_by_score);
5151     boolean entry_is_empty = (entry->score == 0 &&
5152                               entry->time == 0);
5153
5154     // prevent adding server score entries if also existing in local score file
5155     // (special case: historic score entries have an empty tape basename entry)
5156     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5157         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5158     {
5159       // add fields from server score entry not stored in local score entry
5160       // (currently, this means setting platform, version and country fields;
5161       // in rare cases, this may also correct an invalid score value, as
5162       // historic scores might have been truncated to 16-bit values locally)
5163       *entry = *new_entry;
5164
5165       return -1;
5166     }
5167
5168     if (is_better || entry_is_empty)
5169     {
5170       // player has made it to the hall of fame
5171
5172       if (i < MAX_SCORE_ENTRIES - 1)
5173       {
5174         int m = MAX_SCORE_ENTRIES - 1;
5175         int l;
5176
5177         if (one_score_entry_per_name)
5178         {
5179           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5180             if (strEqual(list->entry[l].name, new_entry->name))
5181               m = l;
5182
5183           if (m == i)   // player's new highscore overwrites his old one
5184             goto put_into_list;
5185         }
5186
5187         for (l = m; l > i; l--)
5188           list->entry[l] = list->entry[l - 1];
5189       }
5190
5191       put_into_list:
5192
5193       *entry = *new_entry;
5194
5195       return i;
5196     }
5197     else if (one_score_entry_per_name &&
5198              strEqual(entry->name, new_entry->name))
5199     {
5200       // player already in high score list with better score or time
5201
5202       return -1;
5203     }
5204   }
5205
5206   // special case: new score is beyond the last high score list position
5207   return MAX_SCORE_ENTRIES;
5208 }
5209
5210 void NewHighScore(int level_nr, boolean tape_saved)
5211 {
5212   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5213   boolean one_per_name = FALSE;
5214
5215   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5216   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5217
5218   new_entry.score = game.score_final;
5219   new_entry.time = game.score_time_final;
5220
5221   LoadScore(level_nr);
5222
5223   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5224
5225   if (scores.last_added >= MAX_SCORE_ENTRIES)
5226   {
5227     scores.last_added = MAX_SCORE_ENTRIES - 1;
5228     scores.force_last_added = TRUE;
5229
5230     scores.entry[scores.last_added] = new_entry;
5231
5232     // store last added local score entry (before merging server scores)
5233     scores.last_added_local = scores.last_added;
5234
5235     return;
5236   }
5237
5238   if (scores.last_added < 0)
5239     return;
5240
5241   SaveScore(level_nr);
5242
5243   // store last added local score entry (before merging server scores)
5244   scores.last_added_local = scores.last_added;
5245
5246   if (!game.LevelSolved_SaveTape)
5247     return;
5248
5249   SaveScoreTape(level_nr);
5250
5251   if (setup.ask_for_using_api_server)
5252   {
5253     setup.use_api_server =
5254       Request("Upload your score and tape to the high score server?", REQ_ASK);
5255
5256     if (!setup.use_api_server)
5257       Request("Not using high score server! Use setup menu to enable again!",
5258               REQ_CONFIRM);
5259
5260     runtime.use_api_server = setup.use_api_server;
5261
5262     // after asking for using API server once, do not ask again
5263     setup.ask_for_using_api_server = FALSE;
5264
5265     SaveSetup_ServerSetup();
5266   }
5267
5268   SaveServerScore(level_nr, tape_saved);
5269 }
5270
5271 void MergeServerScore(void)
5272 {
5273   struct ScoreEntry last_added_entry;
5274   boolean one_per_name = FALSE;
5275   int i;
5276
5277   if (scores.last_added >= 0)
5278     last_added_entry = scores.entry[scores.last_added];
5279
5280   for (i = 0; i < server_scores.num_entries; i++)
5281   {
5282     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5283
5284     if (pos >= 0 && pos <= scores.last_added)
5285       scores.last_added++;
5286   }
5287
5288   if (scores.last_added >= MAX_SCORE_ENTRIES)
5289   {
5290     scores.last_added = MAX_SCORE_ENTRIES - 1;
5291     scores.force_last_added = TRUE;
5292
5293     scores.entry[scores.last_added] = last_added_entry;
5294   }
5295 }
5296
5297 static int getElementMoveStepsizeExt(int x, int y, int direction)
5298 {
5299   int element = Tile[x][y];
5300   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5301   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5302   int horiz_move = (dx != 0);
5303   int sign = (horiz_move ? dx : dy);
5304   int step = sign * element_info[element].move_stepsize;
5305
5306   // special values for move stepsize for spring and things on conveyor belt
5307   if (horiz_move)
5308   {
5309     if (CAN_FALL(element) &&
5310         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5311       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5312     else if (element == EL_SPRING)
5313       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5314   }
5315
5316   return step;
5317 }
5318
5319 static int getElementMoveStepsize(int x, int y)
5320 {
5321   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5322 }
5323
5324 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5325 {
5326   if (player->GfxAction != action || player->GfxDir != dir)
5327   {
5328     player->GfxAction = action;
5329     player->GfxDir = dir;
5330     player->Frame = 0;
5331     player->StepFrame = 0;
5332   }
5333 }
5334
5335 static void ResetGfxFrame(int x, int y)
5336 {
5337   // profiling showed that "autotest" spends 10~20% of its time in this function
5338   if (DrawingDeactivatedField())
5339     return;
5340
5341   int element = Tile[x][y];
5342   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5343
5344   if (graphic_info[graphic].anim_global_sync)
5345     GfxFrame[x][y] = FrameCounter;
5346   else if (graphic_info[graphic].anim_global_anim_sync)
5347     GfxFrame[x][y] = getGlobalAnimSyncFrame();
5348   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5349     GfxFrame[x][y] = CustomValue[x][y];
5350   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5351     GfxFrame[x][y] = element_info[element].collect_score;
5352   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5353     GfxFrame[x][y] = ChangeDelay[x][y];
5354 }
5355
5356 static void ResetGfxAnimation(int x, int y)
5357 {
5358   GfxAction[x][y] = ACTION_DEFAULT;
5359   GfxDir[x][y] = MovDir[x][y];
5360   GfxFrame[x][y] = 0;
5361
5362   ResetGfxFrame(x, y);
5363 }
5364
5365 static void ResetRandomAnimationValue(int x, int y)
5366 {
5367   GfxRandom[x][y] = INIT_GFX_RANDOM();
5368 }
5369
5370 static void InitMovingField(int x, int y, int direction)
5371 {
5372   int element = Tile[x][y];
5373   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5374   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5375   int newx = x + dx;
5376   int newy = y + dy;
5377   boolean is_moving_before, is_moving_after;
5378
5379   // check if element was/is moving or being moved before/after mode change
5380   is_moving_before = (WasJustMoving[x][y] != 0);
5381   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5382
5383   // reset animation only for moving elements which change direction of moving
5384   // or which just started or stopped moving
5385   // (else CEs with property "can move" / "not moving" are reset each frame)
5386   if (is_moving_before != is_moving_after ||
5387       direction != MovDir[x][y])
5388     ResetGfxAnimation(x, y);
5389
5390   MovDir[x][y] = direction;
5391   GfxDir[x][y] = direction;
5392
5393   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5394                      direction == MV_DOWN && CAN_FALL(element) ?
5395                      ACTION_FALLING : ACTION_MOVING);
5396
5397   // this is needed for CEs with property "can move" / "not moving"
5398
5399   if (is_moving_after)
5400   {
5401     if (Tile[newx][newy] == EL_EMPTY)
5402       Tile[newx][newy] = EL_BLOCKED;
5403
5404     MovDir[newx][newy] = MovDir[x][y];
5405
5406     CustomValue[newx][newy] = CustomValue[x][y];
5407
5408     GfxFrame[newx][newy] = GfxFrame[x][y];
5409     GfxRandom[newx][newy] = GfxRandom[x][y];
5410     GfxAction[newx][newy] = GfxAction[x][y];
5411     GfxDir[newx][newy] = GfxDir[x][y];
5412   }
5413 }
5414
5415 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5416 {
5417   int direction = MovDir[x][y];
5418   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5419   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5420
5421   *goes_to_x = newx;
5422   *goes_to_y = newy;
5423 }
5424
5425 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5426 {
5427   int direction = MovDir[x][y];
5428   int oldx = x + (direction & MV_LEFT ? +1 : direction & MV_RIGHT ? -1 : 0);
5429   int oldy = y + (direction & MV_UP   ? +1 : direction & MV_DOWN  ? -1 : 0);
5430
5431   *comes_from_x = oldx;
5432   *comes_from_y = oldy;
5433 }
5434
5435 static int MovingOrBlocked2Element(int x, int y)
5436 {
5437   int element = Tile[x][y];
5438
5439   if (element == EL_BLOCKED)
5440   {
5441     int oldx, oldy;
5442
5443     Blocked2Moving(x, y, &oldx, &oldy);
5444
5445     return Tile[oldx][oldy];
5446   }
5447
5448   return element;
5449 }
5450
5451 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5452 {
5453   // like MovingOrBlocked2Element(), but if element is moving
5454   // and (x, y) is the field the moving element is just leaving,
5455   // return EL_BLOCKED instead of the element value
5456   int element = Tile[x][y];
5457
5458   if (IS_MOVING(x, y))
5459   {
5460     if (element == EL_BLOCKED)
5461     {
5462       int oldx, oldy;
5463
5464       Blocked2Moving(x, y, &oldx, &oldy);
5465       return Tile[oldx][oldy];
5466     }
5467     else
5468       return EL_BLOCKED;
5469   }
5470   else
5471     return element;
5472 }
5473
5474 static void RemoveField(int x, int y)
5475 {
5476   Tile[x][y] = EL_EMPTY;
5477
5478   MovPos[x][y] = 0;
5479   MovDir[x][y] = 0;
5480   MovDelay[x][y] = 0;
5481
5482   CustomValue[x][y] = 0;
5483
5484   AmoebaNr[x][y] = 0;
5485   ChangeDelay[x][y] = 0;
5486   ChangePage[x][y] = -1;
5487   Pushed[x][y] = FALSE;
5488
5489   GfxElement[x][y] = EL_UNDEFINED;
5490   GfxAction[x][y] = ACTION_DEFAULT;
5491   GfxDir[x][y] = MV_NONE;
5492 }
5493
5494 static void RemoveMovingField(int x, int y)
5495 {
5496   int oldx = x, oldy = y, newx = x, newy = y;
5497   int element = Tile[x][y];
5498   int next_element = EL_UNDEFINED;
5499
5500   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5501     return;
5502
5503   if (IS_MOVING(x, y))
5504   {
5505     Moving2Blocked(x, y, &newx, &newy);
5506
5507     if (Tile[newx][newy] != EL_BLOCKED)
5508     {
5509       // element is moving, but target field is not free (blocked), but
5510       // already occupied by something different (example: acid pool);
5511       // in this case, only remove the moving field, but not the target
5512
5513       RemoveField(oldx, oldy);
5514
5515       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5516
5517       TEST_DrawLevelField(oldx, oldy);
5518
5519       return;
5520     }
5521   }
5522   else if (element == EL_BLOCKED)
5523   {
5524     Blocked2Moving(x, y, &oldx, &oldy);
5525     if (!IS_MOVING(oldx, oldy))
5526       return;
5527   }
5528
5529   if (element == EL_BLOCKED &&
5530       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5531        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5532        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5533        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5534        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5535        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5536     next_element = get_next_element(Tile[oldx][oldy]);
5537
5538   RemoveField(oldx, oldy);
5539   RemoveField(newx, newy);
5540
5541   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5542
5543   if (next_element != EL_UNDEFINED)
5544     Tile[oldx][oldy] = next_element;
5545
5546   TEST_DrawLevelField(oldx, oldy);
5547   TEST_DrawLevelField(newx, newy);
5548 }
5549
5550 void DrawDynamite(int x, int y)
5551 {
5552   int sx = SCREENX(x), sy = SCREENY(y);
5553   int graphic = el2img(Tile[x][y]);
5554   int frame;
5555
5556   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5557     return;
5558
5559   if (IS_WALKABLE_INSIDE(Back[x][y]))
5560     return;
5561
5562   if (Back[x][y])
5563     DrawLevelElement(x, y, Back[x][y]);
5564   else if (Store[x][y])
5565     DrawLevelElement(x, y, Store[x][y]);
5566   else if (game.use_masked_elements)
5567     DrawLevelElement(x, y, EL_EMPTY);
5568
5569   frame = getGraphicAnimationFrameXY(graphic, x, y);
5570
5571   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5572     DrawGraphicThruMask(sx, sy, graphic, frame);
5573   else
5574     DrawGraphic(sx, sy, graphic, frame);
5575 }
5576
5577 static void CheckDynamite(int x, int y)
5578 {
5579   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5580   {
5581     MovDelay[x][y]--;
5582
5583     if (MovDelay[x][y] != 0)
5584     {
5585       DrawDynamite(x, y);
5586       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5587
5588       return;
5589     }
5590   }
5591
5592   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5593
5594   Bang(x, y);
5595 }
5596
5597 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5598 {
5599   boolean num_checked_players = 0;
5600   int i;
5601
5602   for (i = 0; i < MAX_PLAYERS; i++)
5603   {
5604     if (stored_player[i].active)
5605     {
5606       int sx = stored_player[i].jx;
5607       int sy = stored_player[i].jy;
5608
5609       if (num_checked_players == 0)
5610       {
5611         *sx1 = *sx2 = sx;
5612         *sy1 = *sy2 = sy;
5613       }
5614       else
5615       {
5616         *sx1 = MIN(*sx1, sx);
5617         *sy1 = MIN(*sy1, sy);
5618         *sx2 = MAX(*sx2, sx);
5619         *sy2 = MAX(*sy2, sy);
5620       }
5621
5622       num_checked_players++;
5623     }
5624   }
5625 }
5626
5627 static boolean checkIfAllPlayersFitToScreen_RND(void)
5628 {
5629   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5630
5631   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5632
5633   return (sx2 - sx1 < SCR_FIELDX &&
5634           sy2 - sy1 < SCR_FIELDY);
5635 }
5636
5637 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5638 {
5639   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5640
5641   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5642
5643   *sx = (sx1 + sx2) / 2;
5644   *sy = (sy1 + sy2) / 2;
5645 }
5646
5647 static void DrawRelocateScreen(int old_x, int old_y, int x, int y,
5648                                boolean center_screen, boolean quick_relocation)
5649 {
5650   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5651   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5652   boolean no_delay = (tape.warp_forward);
5653   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5654   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5655   int new_scroll_x, new_scroll_y;
5656
5657   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5658   {
5659     // case 1: quick relocation inside visible screen (without scrolling)
5660
5661     RedrawPlayfield();
5662
5663     return;
5664   }
5665
5666   if (!level.shifted_relocation || center_screen)
5667   {
5668     // relocation _with_ centering of screen
5669
5670     new_scroll_x = SCROLL_POSITION_X(x);
5671     new_scroll_y = SCROLL_POSITION_Y(y);
5672   }
5673   else
5674   {
5675     // relocation _without_ centering of screen
5676
5677     int center_scroll_x = SCROLL_POSITION_X(old_x);
5678     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5679     int offset_x = x + (scroll_x - center_scroll_x);
5680     int offset_y = y + (scroll_y - center_scroll_y);
5681
5682     // for new screen position, apply previous offset to center position
5683     new_scroll_x = SCROLL_POSITION_X(offset_x);
5684     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5685   }
5686
5687   if (quick_relocation)
5688   {
5689     // case 2: quick relocation (redraw without visible scrolling)
5690
5691     scroll_x = new_scroll_x;
5692     scroll_y = new_scroll_y;
5693
5694     RedrawPlayfield();
5695
5696     return;
5697   }
5698
5699   // case 3: visible relocation (with scrolling to new position)
5700
5701   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5702
5703   SetVideoFrameDelay(wait_delay_value);
5704
5705   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5706   {
5707     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5708     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5709
5710     if (dx == 0 && dy == 0)             // no scrolling needed at all
5711       break;
5712
5713     scroll_x -= dx;
5714     scroll_y -= dy;
5715
5716     // set values for horizontal/vertical screen scrolling (half tile size)
5717     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5718     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5719     int pos_x = dx * TILEX / 2;
5720     int pos_y = dy * TILEY / 2;
5721     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5722     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5723
5724     ScrollLevel(dx, dy);
5725     DrawAllPlayers();
5726
5727     // scroll in two steps of half tile size to make things smoother
5728     BlitScreenToBitmapExt_RND(window, fx, fy);
5729
5730     // scroll second step to align at full tile size
5731     BlitScreenToBitmap(window);
5732   }
5733
5734   DrawAllPlayers();
5735   BackToFront();
5736
5737   SetVideoFrameDelay(frame_delay_value_old);
5738 }
5739
5740 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5741 {
5742   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5743   int player_nr = GET_PLAYER_NR(el_player);
5744   struct PlayerInfo *player = &stored_player[player_nr];
5745   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5746   boolean no_delay = (tape.warp_forward);
5747   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5748   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5749   int old_jx = player->jx;
5750   int old_jy = player->jy;
5751   int old_element = Tile[old_jx][old_jy];
5752   int element = Tile[jx][jy];
5753   boolean player_relocated = (old_jx != jx || old_jy != jy);
5754
5755   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5756   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5757   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5758   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5759   int leave_side_horiz = move_dir_horiz;
5760   int leave_side_vert  = move_dir_vert;
5761   int enter_side = enter_side_horiz | enter_side_vert;
5762   int leave_side = leave_side_horiz | leave_side_vert;
5763
5764   if (player->buried)           // do not reanimate dead player
5765     return;
5766
5767   if (!player_relocated)        // no need to relocate the player
5768     return;
5769
5770   if (IS_PLAYER(jx, jy))        // player already placed at new position
5771   {
5772     RemoveField(jx, jy);        // temporarily remove newly placed player
5773     DrawLevelField(jx, jy);
5774   }
5775
5776   if (player->present)
5777   {
5778     while (player->MovPos)
5779     {
5780       ScrollPlayer(player, SCROLL_GO_ON);
5781       ScrollScreen(NULL, SCROLL_GO_ON);
5782
5783       AdvanceFrameAndPlayerCounters(player->index_nr);
5784
5785       DrawPlayer(player);
5786
5787       BackToFront_WithFrameDelay(wait_delay_value);
5788     }
5789
5790     DrawPlayer(player);         // needed here only to cleanup last field
5791     DrawLevelField(player->jx, player->jy);     // remove player graphic
5792
5793     player->is_moving = FALSE;
5794   }
5795
5796   if (IS_CUSTOM_ELEMENT(old_element))
5797     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5798                                CE_LEFT_BY_PLAYER,
5799                                player->index_bit, leave_side);
5800
5801   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5802                                       CE_PLAYER_LEAVES_X,
5803                                       player->index_bit, leave_side);
5804
5805   Tile[jx][jy] = el_player;
5806   InitPlayerField(jx, jy, el_player, TRUE);
5807
5808   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5809      possible that the relocation target field did not contain a player element,
5810      but a walkable element, to which the new player was relocated -- in this
5811      case, restore that (already initialized!) element on the player field */
5812   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5813   {
5814     Tile[jx][jy] = element;     // restore previously existing element
5815   }
5816
5817   // only visually relocate centered player
5818   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy,
5819                      FALSE, level.instant_relocation);
5820
5821   TestIfPlayerTouchesBadThing(jx, jy);
5822   TestIfPlayerTouchesCustomElement(jx, jy);
5823
5824   if (IS_CUSTOM_ELEMENT(element))
5825     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5826                                player->index_bit, enter_side);
5827
5828   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5829                                       player->index_bit, enter_side);
5830
5831   if (player->is_switching)
5832   {
5833     /* ensure that relocation while still switching an element does not cause
5834        a new element to be treated as also switched directly after relocation
5835        (this is important for teleporter switches that teleport the player to
5836        a place where another teleporter switch is in the same direction, which
5837        would then incorrectly be treated as immediately switched before the
5838        direction key that caused the switch was released) */
5839
5840     player->switch_x += jx - old_jx;
5841     player->switch_y += jy - old_jy;
5842   }
5843 }
5844
5845 static void Explode(int ex, int ey, int phase, int mode)
5846 {
5847   int x, y;
5848   int last_phase;
5849   int border_element;
5850
5851   if (game.explosions_delayed)
5852   {
5853     ExplodeField[ex][ey] = mode;
5854     return;
5855   }
5856
5857   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5858   {
5859     int center_element = Tile[ex][ey];
5860     int ce_value = CustomValue[ex][ey];
5861     int ce_score = element_info[center_element].collect_score;
5862     int artwork_element, explosion_element;     // set these values later
5863
5864     // remove things displayed in background while burning dynamite
5865     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5866       Back[ex][ey] = 0;
5867
5868     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5869     {
5870       // put moving element to center field (and let it explode there)
5871       center_element = MovingOrBlocked2Element(ex, ey);
5872       RemoveMovingField(ex, ey);
5873       Tile[ex][ey] = center_element;
5874     }
5875
5876     // now "center_element" is finally determined -- set related values now
5877     artwork_element = center_element;           // for custom player artwork
5878     explosion_element = center_element;         // for custom player artwork
5879
5880     if (IS_PLAYER(ex, ey))
5881     {
5882       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5883
5884       artwork_element = stored_player[player_nr].artwork_element;
5885
5886       if (level.use_explosion_element[player_nr])
5887       {
5888         explosion_element = level.explosion_element[player_nr];
5889         artwork_element = explosion_element;
5890       }
5891     }
5892
5893     if (mode == EX_TYPE_NORMAL ||
5894         mode == EX_TYPE_CENTER ||
5895         mode == EX_TYPE_CROSS)
5896       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5897
5898     last_phase = element_info[explosion_element].explosion_delay + 1;
5899
5900     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5901     {
5902       int xx = x - ex + 1;
5903       int yy = y - ey + 1;
5904       int element;
5905
5906       if (!IN_LEV_FIELD(x, y) ||
5907           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5908           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5909         continue;
5910
5911       element = Tile[x][y];
5912
5913       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5914       {
5915         element = MovingOrBlocked2Element(x, y);
5916
5917         if (!IS_EXPLOSION_PROOF(element))
5918           RemoveMovingField(x, y);
5919       }
5920
5921       // indestructible elements can only explode in center (but not flames)
5922       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5923                                            mode == EX_TYPE_BORDER)) ||
5924           element == EL_FLAMES)
5925         continue;
5926
5927       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5928          behaviour, for example when touching a yamyam that explodes to rocks
5929          with active deadly shield, a rock is created under the player !!! */
5930       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5931 #if 0
5932       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5933           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5934            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5935 #else
5936       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5937 #endif
5938       {
5939         if (IS_ACTIVE_BOMB(element))
5940         {
5941           // re-activate things under the bomb like gate or penguin
5942           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5943           Back[x][y] = 0;
5944         }
5945
5946         continue;
5947       }
5948
5949       // save walkable background elements while explosion on same tile
5950       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5951           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5952         Back[x][y] = element;
5953
5954       // ignite explodable elements reached by other explosion
5955       if (element == EL_EXPLOSION)
5956         element = Store2[x][y];
5957
5958       if (AmoebaNr[x][y] &&
5959           (element == EL_AMOEBA_FULL ||
5960            element == EL_BD_AMOEBA ||
5961            element == EL_AMOEBA_GROWING))
5962       {
5963         AmoebaCnt[AmoebaNr[x][y]]--;
5964         AmoebaCnt2[AmoebaNr[x][y]]--;
5965       }
5966
5967       RemoveField(x, y);
5968
5969       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5970       {
5971         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5972
5973         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5974
5975         if (PLAYERINFO(ex, ey)->use_murphy)
5976           Store[x][y] = EL_EMPTY;
5977       }
5978
5979       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5980       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5981       else if (IS_PLAYER_ELEMENT(center_element))
5982         Store[x][y] = EL_EMPTY;
5983       else if (center_element == EL_YAMYAM)
5984         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5985       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5986         Store[x][y] = element_info[center_element].content.e[xx][yy];
5987 #if 1
5988       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5989       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5990       // otherwise) -- FIX THIS !!!
5991       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5992         Store[x][y] = element_info[element].content.e[1][1];
5993 #else
5994       else if (!CAN_EXPLODE(element))
5995         Store[x][y] = element_info[element].content.e[1][1];
5996 #endif
5997       else
5998         Store[x][y] = EL_EMPTY;
5999
6000       if (IS_CUSTOM_ELEMENT(center_element))
6001         Store[x][y] = (Store[x][y] == EL_CURRENT_CE_VALUE ? ce_value :
6002                        Store[x][y] == EL_CURRENT_CE_SCORE ? ce_score :
6003                        Store[x][y] >= EL_PREV_CE_8 &&
6004                        Store[x][y] <= EL_NEXT_CE_8 ?
6005                        RESOLVED_REFERENCE_ELEMENT(center_element, Store[x][y]) :
6006                        Store[x][y]);
6007
6008       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6009           center_element == EL_AMOEBA_TO_DIAMOND)
6010         Store2[x][y] = element;
6011
6012       Tile[x][y] = EL_EXPLOSION;
6013       GfxElement[x][y] = artwork_element;
6014
6015       ExplodePhase[x][y] = 1;
6016       ExplodeDelay[x][y] = last_phase;
6017
6018       Stop[x][y] = TRUE;
6019     }
6020
6021     if (center_element == EL_YAMYAM)
6022       game.yamyam_content_nr =
6023         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6024
6025     return;
6026   }
6027
6028   if (Stop[ex][ey])
6029     return;
6030
6031   x = ex;
6032   y = ey;
6033
6034   if (phase == 1)
6035     GfxFrame[x][y] = 0;         // restart explosion animation
6036
6037   last_phase = ExplodeDelay[x][y];
6038
6039   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6040
6041   // this can happen if the player leaves an explosion just in time
6042   if (GfxElement[x][y] == EL_UNDEFINED)
6043     GfxElement[x][y] = EL_EMPTY;
6044
6045   border_element = Store2[x][y];
6046   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6047     border_element = StorePlayer[x][y];
6048
6049   if (phase == element_info[border_element].ignition_delay ||
6050       phase == last_phase)
6051   {
6052     boolean border_explosion = FALSE;
6053
6054     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6055         !PLAYER_EXPLOSION_PROTECTED(x, y))
6056     {
6057       KillPlayerUnlessExplosionProtected(x, y);
6058       border_explosion = TRUE;
6059     }
6060     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6061     {
6062       Tile[x][y] = Store2[x][y];
6063       Store2[x][y] = 0;
6064       Bang(x, y);
6065       border_explosion = TRUE;
6066     }
6067     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6068     {
6069       AmoebaToDiamond(x, y);
6070       Store2[x][y] = 0;
6071       border_explosion = TRUE;
6072     }
6073
6074     // if an element just explodes due to another explosion (chain-reaction),
6075     // do not immediately end the new explosion when it was the last frame of
6076     // the explosion (as it would be done in the following "if"-statement!)
6077     if (border_explosion && phase == last_phase)
6078       return;
6079   }
6080
6081   // this can happen if the player was just killed by an explosion
6082   if (GfxElement[x][y] == EL_UNDEFINED)
6083     GfxElement[x][y] = EL_EMPTY;
6084
6085   if (phase == last_phase)
6086   {
6087     int element;
6088
6089     element = Tile[x][y] = Store[x][y];
6090     Store[x][y] = Store2[x][y] = 0;
6091     GfxElement[x][y] = EL_UNDEFINED;
6092
6093     // player can escape from explosions and might therefore be still alive
6094     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6095         element <= EL_PLAYER_IS_EXPLODING_4)
6096     {
6097       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6098       int explosion_element = EL_PLAYER_1 + player_nr;
6099       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6100       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6101
6102       if (level.use_explosion_element[player_nr])
6103         explosion_element = level.explosion_element[player_nr];
6104
6105       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6106                     element_info[explosion_element].content.e[xx][yy]);
6107     }
6108
6109     // restore probably existing indestructible background element
6110     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6111       element = Tile[x][y] = Back[x][y];
6112     Back[x][y] = 0;
6113
6114     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6115     GfxDir[x][y] = MV_NONE;
6116     ChangeDelay[x][y] = 0;
6117     ChangePage[x][y] = -1;
6118
6119     CustomValue[x][y] = 0;
6120
6121     InitField_WithBug2(x, y, FALSE);
6122
6123     TEST_DrawLevelField(x, y);
6124
6125     TestIfElementTouchesCustomElement(x, y);
6126
6127     if (GFX_CRUMBLED(element))
6128       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6129
6130     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6131       StorePlayer[x][y] = 0;
6132
6133     if (IS_PLAYER_ELEMENT(element))
6134       RelocatePlayer(x, y, element);
6135   }
6136   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6137   {
6138     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6139     int frame = getGraphicAnimationFrameXY(graphic, x, y);
6140
6141     if (phase == 1)
6142       TEST_DrawLevelFieldCrumbled(x, y);
6143
6144     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6145     {
6146       DrawLevelElement(x, y, Back[x][y]);
6147       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6148     }
6149     else if (IS_WALKABLE_UNDER(Back[x][y]))
6150     {
6151       DrawLevelGraphic(x, y, graphic, frame);
6152       DrawLevelElementThruMask(x, y, Back[x][y]);
6153     }
6154     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6155       DrawLevelGraphic(x, y, graphic, frame);
6156   }
6157 }
6158
6159 static void DynaExplode(int ex, int ey)
6160 {
6161   int i, j;
6162   int dynabomb_element = Tile[ex][ey];
6163   int dynabomb_size = 1;
6164   boolean dynabomb_xl = FALSE;
6165   struct PlayerInfo *player;
6166   struct XY *xy = xy_topdown;
6167
6168   if (IS_ACTIVE_BOMB(dynabomb_element))
6169   {
6170     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6171     dynabomb_size = player->dynabomb_size;
6172     dynabomb_xl = player->dynabomb_xl;
6173     player->dynabombs_left++;
6174   }
6175
6176   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6177
6178   for (i = 0; i < NUM_DIRECTIONS; i++)
6179   {
6180     for (j = 1; j <= dynabomb_size; j++)
6181     {
6182       int x = ex + j * xy[i].x;
6183       int y = ey + j * xy[i].y;
6184       int element;
6185
6186       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6187         break;
6188
6189       element = Tile[x][y];
6190
6191       // do not restart explosions of fields with active bombs
6192       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6193         continue;
6194
6195       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6196
6197       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6198           !IS_DIGGABLE(element) && !dynabomb_xl)
6199         break;
6200     }
6201   }
6202 }
6203
6204 void Bang(int x, int y)
6205 {
6206   int element = MovingOrBlocked2Element(x, y);
6207   int explosion_type = EX_TYPE_NORMAL;
6208
6209   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6210   {
6211     struct PlayerInfo *player = PLAYERINFO(x, y);
6212
6213     element = Tile[x][y] = player->initial_element;
6214
6215     if (level.use_explosion_element[player->index_nr])
6216     {
6217       int explosion_element = level.explosion_element[player->index_nr];
6218
6219       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6220         explosion_type = EX_TYPE_CROSS;
6221       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6222         explosion_type = EX_TYPE_CENTER;
6223     }
6224   }
6225
6226   switch (element)
6227   {
6228     case EL_BUG:
6229     case EL_SPACESHIP:
6230     case EL_BD_BUTTERFLY:
6231     case EL_BD_FIREFLY:
6232     case EL_YAMYAM:
6233     case EL_DARK_YAMYAM:
6234     case EL_ROBOT:
6235     case EL_PACMAN:
6236     case EL_MOLE:
6237       RaiseScoreElement(element);
6238       break;
6239
6240     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6241     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6242     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6243     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6244     case EL_DYNABOMB_INCREASE_NUMBER:
6245     case EL_DYNABOMB_INCREASE_SIZE:
6246     case EL_DYNABOMB_INCREASE_POWER:
6247       explosion_type = EX_TYPE_DYNA;
6248       break;
6249
6250     case EL_DC_LANDMINE:
6251       explosion_type = EX_TYPE_CENTER;
6252       break;
6253
6254     case EL_PENGUIN:
6255     case EL_LAMP:
6256     case EL_LAMP_ACTIVE:
6257     case EL_AMOEBA_TO_DIAMOND:
6258       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6259         explosion_type = EX_TYPE_CENTER;
6260       break;
6261
6262     default:
6263       if (element_info[element].explosion_type == EXPLODES_CROSS)
6264         explosion_type = EX_TYPE_CROSS;
6265       else if (element_info[element].explosion_type == EXPLODES_1X1)
6266         explosion_type = EX_TYPE_CENTER;
6267       break;
6268   }
6269
6270   if (explosion_type == EX_TYPE_DYNA)
6271     DynaExplode(x, y);
6272   else
6273     Explode(x, y, EX_PHASE_START, explosion_type);
6274
6275   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6276 }
6277
6278 static void SplashAcid(int x, int y)
6279 {
6280   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6281       (!IN_LEV_FIELD(x - 1, y - 2) ||
6282        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6283     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6284
6285   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6286       (!IN_LEV_FIELD(x + 1, y - 2) ||
6287        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6288     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6289
6290   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6291 }
6292
6293 static void InitBeltMovement(void)
6294 {
6295   static int belt_base_element[4] =
6296   {
6297     EL_CONVEYOR_BELT_1_LEFT,
6298     EL_CONVEYOR_BELT_2_LEFT,
6299     EL_CONVEYOR_BELT_3_LEFT,
6300     EL_CONVEYOR_BELT_4_LEFT
6301   };
6302   static int belt_base_active_element[4] =
6303   {
6304     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6305     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6306     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6307     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6308   };
6309
6310   int x, y, i, j;
6311
6312   // set frame order for belt animation graphic according to belt direction
6313   for (i = 0; i < NUM_BELTS; i++)
6314   {
6315     int belt_nr = i;
6316
6317     for (j = 0; j < NUM_BELT_PARTS; j++)
6318     {
6319       int element = belt_base_active_element[belt_nr] + j;
6320       int graphic_1 = el2img(element);
6321       int graphic_2 = el2panelimg(element);
6322
6323       if (game.belt_dir[i] == MV_LEFT)
6324       {
6325         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6326         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6327       }
6328       else
6329       {
6330         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6331         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6332       }
6333     }
6334   }
6335
6336   SCAN_PLAYFIELD(x, y)
6337   {
6338     int element = Tile[x][y];
6339
6340     for (i = 0; i < NUM_BELTS; i++)
6341     {
6342       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6343       {
6344         int e_belt_nr = getBeltNrFromBeltElement(element);
6345         int belt_nr = i;
6346
6347         if (e_belt_nr == belt_nr)
6348         {
6349           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6350
6351           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6352         }
6353       }
6354     }
6355   }
6356 }
6357
6358 static void ToggleBeltSwitch(int x, int y)
6359 {
6360   static int belt_base_element[4] =
6361   {
6362     EL_CONVEYOR_BELT_1_LEFT,
6363     EL_CONVEYOR_BELT_2_LEFT,
6364     EL_CONVEYOR_BELT_3_LEFT,
6365     EL_CONVEYOR_BELT_4_LEFT
6366   };
6367   static int belt_base_active_element[4] =
6368   {
6369     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6370     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6371     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6372     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6373   };
6374   static int belt_base_switch_element[4] =
6375   {
6376     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6377     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6378     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6379     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6380   };
6381   static int belt_move_dir[4] =
6382   {
6383     MV_LEFT,
6384     MV_NONE,
6385     MV_RIGHT,
6386     MV_NONE,
6387   };
6388
6389   int element = Tile[x][y];
6390   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6391   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6392   int belt_dir = belt_move_dir[belt_dir_nr];
6393   int xx, yy, i;
6394
6395   if (!IS_BELT_SWITCH(element))
6396     return;
6397
6398   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6399   game.belt_dir[belt_nr] = belt_dir;
6400
6401   if (belt_dir_nr == 3)
6402     belt_dir_nr = 1;
6403
6404   // set frame order for belt animation graphic according to belt direction
6405   for (i = 0; i < NUM_BELT_PARTS; i++)
6406   {
6407     int element = belt_base_active_element[belt_nr] + i;
6408     int graphic_1 = el2img(element);
6409     int graphic_2 = el2panelimg(element);
6410
6411     if (belt_dir == MV_LEFT)
6412     {
6413       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6414       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6415     }
6416     else
6417     {
6418       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6419       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6420     }
6421   }
6422
6423   SCAN_PLAYFIELD(xx, yy)
6424   {
6425     int element = Tile[xx][yy];
6426
6427     if (IS_BELT_SWITCH(element))
6428     {
6429       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6430
6431       if (e_belt_nr == belt_nr)
6432       {
6433         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6434         TEST_DrawLevelField(xx, yy);
6435       }
6436     }
6437     else if (IS_BELT(element) && belt_dir != MV_NONE)
6438     {
6439       int e_belt_nr = getBeltNrFromBeltElement(element);
6440
6441       if (e_belt_nr == belt_nr)
6442       {
6443         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6444
6445         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6446         TEST_DrawLevelField(xx, yy);
6447       }
6448     }
6449     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6450     {
6451       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6452
6453       if (e_belt_nr == belt_nr)
6454       {
6455         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6456
6457         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6458         TEST_DrawLevelField(xx, yy);
6459       }
6460     }
6461   }
6462 }
6463
6464 static void ToggleSwitchgateSwitch(void)
6465 {
6466   int xx, yy;
6467
6468   game.switchgate_pos = !game.switchgate_pos;
6469
6470   SCAN_PLAYFIELD(xx, yy)
6471   {
6472     int element = Tile[xx][yy];
6473
6474     if (element == EL_SWITCHGATE_SWITCH_UP)
6475     {
6476       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6477       TEST_DrawLevelField(xx, yy);
6478     }
6479     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6480     {
6481       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6482       TEST_DrawLevelField(xx, yy);
6483     }
6484     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6485     {
6486       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6487       TEST_DrawLevelField(xx, yy);
6488     }
6489     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6490     {
6491       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6492       TEST_DrawLevelField(xx, yy);
6493     }
6494     else if (element == EL_SWITCHGATE_OPEN ||
6495              element == EL_SWITCHGATE_OPENING)
6496     {
6497       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6498
6499       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6500     }
6501     else if (element == EL_SWITCHGATE_CLOSED ||
6502              element == EL_SWITCHGATE_CLOSING)
6503     {
6504       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6505
6506       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6507     }
6508   }
6509 }
6510
6511 static int getInvisibleActiveFromInvisibleElement(int element)
6512 {
6513   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6514           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6515           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6516           element);
6517 }
6518
6519 static int getInvisibleFromInvisibleActiveElement(int element)
6520 {
6521   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6522           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6523           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6524           element);
6525 }
6526
6527 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6528 {
6529   int x, y;
6530
6531   SCAN_PLAYFIELD(x, y)
6532   {
6533     int element = Tile[x][y];
6534
6535     if (element == EL_LIGHT_SWITCH &&
6536         game.light_time_left > 0)
6537     {
6538       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6539       TEST_DrawLevelField(x, y);
6540     }
6541     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6542              game.light_time_left == 0)
6543     {
6544       Tile[x][y] = EL_LIGHT_SWITCH;
6545       TEST_DrawLevelField(x, y);
6546     }
6547     else if (element == EL_EMC_DRIPPER &&
6548              game.light_time_left > 0)
6549     {
6550       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6551       TEST_DrawLevelField(x, y);
6552     }
6553     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6554              game.light_time_left == 0)
6555     {
6556       Tile[x][y] = EL_EMC_DRIPPER;
6557       TEST_DrawLevelField(x, y);
6558     }
6559     else if (element == EL_INVISIBLE_STEELWALL ||
6560              element == EL_INVISIBLE_WALL ||
6561              element == EL_INVISIBLE_SAND)
6562     {
6563       if (game.light_time_left > 0)
6564         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6565
6566       TEST_DrawLevelField(x, y);
6567
6568       // uncrumble neighbour fields, if needed
6569       if (element == EL_INVISIBLE_SAND)
6570         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6571     }
6572     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6573              element == EL_INVISIBLE_WALL_ACTIVE ||
6574              element == EL_INVISIBLE_SAND_ACTIVE)
6575     {
6576       if (game.light_time_left == 0)
6577         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6578
6579       TEST_DrawLevelField(x, y);
6580
6581       // re-crumble neighbour fields, if needed
6582       if (element == EL_INVISIBLE_SAND)
6583         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6584     }
6585   }
6586 }
6587
6588 static void RedrawAllInvisibleElementsForLenses(void)
6589 {
6590   int x, y;
6591
6592   SCAN_PLAYFIELD(x, y)
6593   {
6594     int element = Tile[x][y];
6595
6596     if (element == EL_EMC_DRIPPER &&
6597         game.lenses_time_left > 0)
6598     {
6599       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6600       TEST_DrawLevelField(x, y);
6601     }
6602     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6603              game.lenses_time_left == 0)
6604     {
6605       Tile[x][y] = EL_EMC_DRIPPER;
6606       TEST_DrawLevelField(x, y);
6607     }
6608     else if (element == EL_INVISIBLE_STEELWALL ||
6609              element == EL_INVISIBLE_WALL ||
6610              element == EL_INVISIBLE_SAND)
6611     {
6612       if (game.lenses_time_left > 0)
6613         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6614
6615       TEST_DrawLevelField(x, y);
6616
6617       // uncrumble neighbour fields, if needed
6618       if (element == EL_INVISIBLE_SAND)
6619         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6620     }
6621     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6622              element == EL_INVISIBLE_WALL_ACTIVE ||
6623              element == EL_INVISIBLE_SAND_ACTIVE)
6624     {
6625       if (game.lenses_time_left == 0)
6626         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6627
6628       TEST_DrawLevelField(x, y);
6629
6630       // re-crumble neighbour fields, if needed
6631       if (element == EL_INVISIBLE_SAND)
6632         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6633     }
6634   }
6635 }
6636
6637 static void RedrawAllInvisibleElementsForMagnifier(void)
6638 {
6639   int x, y;
6640
6641   SCAN_PLAYFIELD(x, y)
6642   {
6643     int element = Tile[x][y];
6644
6645     if (element == EL_EMC_FAKE_GRASS &&
6646         game.magnify_time_left > 0)
6647     {
6648       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6649       TEST_DrawLevelField(x, y);
6650     }
6651     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6652              game.magnify_time_left == 0)
6653     {
6654       Tile[x][y] = EL_EMC_FAKE_GRASS;
6655       TEST_DrawLevelField(x, y);
6656     }
6657     else if (IS_GATE_GRAY(element) &&
6658              game.magnify_time_left > 0)
6659     {
6660       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6661                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6662                     IS_EM_GATE_GRAY(element) ?
6663                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6664                     IS_EMC_GATE_GRAY(element) ?
6665                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6666                     IS_DC_GATE_GRAY(element) ?
6667                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6668                     element);
6669       TEST_DrawLevelField(x, y);
6670     }
6671     else if (IS_GATE_GRAY_ACTIVE(element) &&
6672              game.magnify_time_left == 0)
6673     {
6674       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6675                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6676                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6677                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6678                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6679                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6680                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6681                     EL_DC_GATE_WHITE_GRAY :
6682                     element);
6683       TEST_DrawLevelField(x, y);
6684     }
6685   }
6686 }
6687
6688 static void ToggleLightSwitch(int x, int y)
6689 {
6690   int element = Tile[x][y];
6691
6692   game.light_time_left =
6693     (element == EL_LIGHT_SWITCH ?
6694      level.time_light * FRAMES_PER_SECOND : 0);
6695
6696   RedrawAllLightSwitchesAndInvisibleElements();
6697 }
6698
6699 static void ActivateTimegateSwitch(int x, int y)
6700 {
6701   int xx, yy;
6702
6703   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6704
6705   SCAN_PLAYFIELD(xx, yy)
6706   {
6707     int element = Tile[xx][yy];
6708
6709     if (element == EL_TIMEGATE_CLOSED ||
6710         element == EL_TIMEGATE_CLOSING)
6711     {
6712       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6713       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6714     }
6715
6716     /*
6717     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6718     {
6719       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6720       TEST_DrawLevelField(xx, yy);
6721     }
6722     */
6723
6724   }
6725
6726   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6727                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6728 }
6729
6730 static void Impact(int x, int y)
6731 {
6732   boolean last_line = (y == lev_fieldy - 1);
6733   boolean object_hit = FALSE;
6734   boolean impact = (last_line || object_hit);
6735   int element = Tile[x][y];
6736   int smashed = EL_STEELWALL;
6737
6738   if (!last_line)       // check if element below was hit
6739   {
6740     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6741       return;
6742
6743     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6744                                          MovDir[x][y + 1] != MV_DOWN ||
6745                                          MovPos[x][y + 1] <= TILEY / 2));
6746
6747     // do not smash moving elements that left the smashed field in time
6748     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6749         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6750       object_hit = FALSE;
6751
6752 #if USE_QUICKSAND_IMPACT_BUGFIX
6753     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6754     {
6755       RemoveMovingField(x, y + 1);
6756       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6757       Tile[x][y + 2] = EL_ROCK;
6758       TEST_DrawLevelField(x, y + 2);
6759
6760       object_hit = TRUE;
6761     }
6762
6763     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6764     {
6765       RemoveMovingField(x, y + 1);
6766       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6767       Tile[x][y + 2] = EL_ROCK;
6768       TEST_DrawLevelField(x, y + 2);
6769
6770       object_hit = TRUE;
6771     }
6772 #endif
6773
6774     if (object_hit)
6775       smashed = MovingOrBlocked2Element(x, y + 1);
6776
6777     impact = (last_line || object_hit);
6778   }
6779
6780   if (!last_line && smashed == EL_ACID) // element falls into acid
6781   {
6782     SplashAcid(x, y + 1);
6783     return;
6784   }
6785
6786   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6787   // only reset graphic animation if graphic really changes after impact
6788   if (impact &&
6789       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6790   {
6791     ResetGfxAnimation(x, y);
6792     TEST_DrawLevelField(x, y);
6793   }
6794
6795   if (impact && CAN_EXPLODE_IMPACT(element))
6796   {
6797     Bang(x, y);
6798     return;
6799   }
6800   else if (impact && element == EL_PEARL &&
6801            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6802   {
6803     ResetGfxAnimation(x, y);
6804
6805     Tile[x][y] = EL_PEARL_BREAKING;
6806     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6807     return;
6808   }
6809   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6810   {
6811     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6812
6813     return;
6814   }
6815
6816   if (impact && element == EL_AMOEBA_DROP)
6817   {
6818     if (object_hit && IS_PLAYER(x, y + 1))
6819       KillPlayerUnlessEnemyProtected(x, y + 1);
6820     else if (object_hit && smashed == EL_PENGUIN)
6821       Bang(x, y + 1);
6822     else
6823     {
6824       Tile[x][y] = EL_AMOEBA_GROWING;
6825       Store[x][y] = EL_AMOEBA_WET;
6826
6827       ResetRandomAnimationValue(x, y);
6828     }
6829     return;
6830   }
6831
6832   if (object_hit)               // check which object was hit
6833   {
6834     if ((CAN_PASS_MAGIC_WALL(element) && 
6835          (smashed == EL_MAGIC_WALL ||
6836           smashed == EL_BD_MAGIC_WALL)) ||
6837         (CAN_PASS_DC_MAGIC_WALL(element) &&
6838          smashed == EL_DC_MAGIC_WALL))
6839     {
6840       int xx, yy;
6841       int activated_magic_wall =
6842         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6843          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6844          EL_DC_MAGIC_WALL_ACTIVE);
6845
6846       // activate magic wall / mill
6847       SCAN_PLAYFIELD(xx, yy)
6848       {
6849         if (Tile[xx][yy] == smashed)
6850           Tile[xx][yy] = activated_magic_wall;
6851       }
6852
6853       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6854       game.magic_wall_active = TRUE;
6855
6856       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6857                             SND_MAGIC_WALL_ACTIVATING :
6858                             smashed == EL_BD_MAGIC_WALL ?
6859                             SND_BD_MAGIC_WALL_ACTIVATING :
6860                             SND_DC_MAGIC_WALL_ACTIVATING));
6861     }
6862
6863     if (IS_PLAYER(x, y + 1))
6864     {
6865       if (CAN_SMASH_PLAYER(element))
6866       {
6867         KillPlayerUnlessEnemyProtected(x, y + 1);
6868         return;
6869       }
6870     }
6871     else if (smashed == EL_PENGUIN)
6872     {
6873       if (CAN_SMASH_PLAYER(element))
6874       {
6875         Bang(x, y + 1);
6876         return;
6877       }
6878     }
6879     else if (element == EL_BD_DIAMOND)
6880     {
6881       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6882       {
6883         Bang(x, y + 1);
6884         return;
6885       }
6886     }
6887     else if (((element == EL_SP_INFOTRON ||
6888                element == EL_SP_ZONK) &&
6889               (smashed == EL_SP_SNIKSNAK ||
6890                smashed == EL_SP_ELECTRON ||
6891                smashed == EL_SP_DISK_ORANGE)) ||
6892              (element == EL_SP_INFOTRON &&
6893               smashed == EL_SP_DISK_YELLOW))
6894     {
6895       Bang(x, y + 1);
6896       return;
6897     }
6898     else if (CAN_SMASH_EVERYTHING(element))
6899     {
6900       if (IS_CLASSIC_ENEMY(smashed) ||
6901           CAN_EXPLODE_SMASHED(smashed))
6902       {
6903         Bang(x, y + 1);
6904         return;
6905       }
6906       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6907       {
6908         if (smashed == EL_LAMP ||
6909             smashed == EL_LAMP_ACTIVE)
6910         {
6911           Bang(x, y + 1);
6912           return;
6913         }
6914         else if (smashed == EL_NUT)
6915         {
6916           Tile[x][y + 1] = EL_NUT_BREAKING;
6917           PlayLevelSound(x, y, SND_NUT_BREAKING);
6918           RaiseScoreElement(EL_NUT);
6919           return;
6920         }
6921         else if (smashed == EL_PEARL)
6922         {
6923           ResetGfxAnimation(x, y);
6924
6925           Tile[x][y + 1] = EL_PEARL_BREAKING;
6926           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6927           return;
6928         }
6929         else if (smashed == EL_DIAMOND)
6930         {
6931           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6932           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6933           return;
6934         }
6935         else if (IS_BELT_SWITCH(smashed))
6936         {
6937           ToggleBeltSwitch(x, y + 1);
6938         }
6939         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6940                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6941                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6942                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6943         {
6944           ToggleSwitchgateSwitch();
6945         }
6946         else if (smashed == EL_LIGHT_SWITCH ||
6947                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6948         {
6949           ToggleLightSwitch(x, y + 1);
6950         }
6951         else
6952         {
6953           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6954
6955           CheckElementChangeBySide(x, y + 1, smashed, element,
6956                                    CE_SWITCHED, CH_SIDE_TOP);
6957           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6958                                             CH_SIDE_TOP);
6959         }
6960       }
6961       else
6962       {
6963         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6964       }
6965     }
6966   }
6967
6968   // play sound of magic wall / mill
6969   if (!last_line &&
6970       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6971        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6972        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6973   {
6974     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6975       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6976     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6977       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6978     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6979       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6980
6981     return;
6982   }
6983
6984   // play sound of object that hits the ground
6985   if (last_line || object_hit)
6986     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6987 }
6988
6989 static void TurnRoundExt(int x, int y)
6990 {
6991   static struct
6992   {
6993     int dx, dy;
6994   } move_xy[] =
6995   {
6996     {  0,  0 },
6997     { -1,  0 },
6998     { +1,  0 },
6999     {  0,  0 },
7000     {  0, -1 },
7001     {  0,  0 }, { 0, 0 }, { 0, 0 },
7002     {  0, +1 }
7003   };
7004   static struct
7005   {
7006     int left, right, back;
7007   } turn[] =
7008   {
7009     { 0,        0,              0        },
7010     { MV_DOWN,  MV_UP,          MV_RIGHT },
7011     { MV_UP,    MV_DOWN,        MV_LEFT  },
7012     { 0,        0,              0        },
7013     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
7014     { 0,        0,              0        },
7015     { 0,        0,              0        },
7016     { 0,        0,              0        },
7017     { MV_RIGHT, MV_LEFT,        MV_UP    }
7018   };
7019
7020   int element = Tile[x][y];
7021   int move_pattern = element_info[element].move_pattern;
7022
7023   int old_move_dir = MovDir[x][y];
7024   int left_dir  = turn[old_move_dir].left;
7025   int right_dir = turn[old_move_dir].right;
7026   int back_dir  = turn[old_move_dir].back;
7027
7028   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7029   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7030   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7031   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7032
7033   int left_x  = x + left_dx,  left_y  = y + left_dy;
7034   int right_x = x + right_dx, right_y = y + right_dy;
7035   int move_x  = x + move_dx,  move_y  = y + move_dy;
7036
7037   int xx, yy;
7038
7039   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7040   {
7041     TestIfBadThingTouchesOtherBadThing(x, y);
7042
7043     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7044       MovDir[x][y] = right_dir;
7045     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7046       MovDir[x][y] = left_dir;
7047
7048     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7049       MovDelay[x][y] = 9;
7050     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
7051       MovDelay[x][y] = 1;
7052   }
7053   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7054   {
7055     TestIfBadThingTouchesOtherBadThing(x, y);
7056
7057     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7058       MovDir[x][y] = left_dir;
7059     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7060       MovDir[x][y] = right_dir;
7061
7062     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7063       MovDelay[x][y] = 9;
7064     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
7065       MovDelay[x][y] = 1;
7066   }
7067   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7068   {
7069     TestIfBadThingTouchesOtherBadThing(x, y);
7070
7071     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7072       MovDir[x][y] = left_dir;
7073     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7074       MovDir[x][y] = right_dir;
7075
7076     if (MovDir[x][y] != old_move_dir)
7077       MovDelay[x][y] = 9;
7078   }
7079   else if (element == EL_YAMYAM)
7080   {
7081     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7082     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7083
7084     if (can_turn_left && can_turn_right)
7085       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7086     else if (can_turn_left)
7087       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7088     else if (can_turn_right)
7089       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7090     else
7091       MovDir[x][y] = back_dir;
7092
7093     MovDelay[x][y] = 16 + 16 * RND(3);
7094   }
7095   else if (element == EL_DARK_YAMYAM)
7096   {
7097     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7098                                                          left_x, left_y);
7099     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7100                                                          right_x, right_y);
7101
7102     if (can_turn_left && can_turn_right)
7103       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7104     else if (can_turn_left)
7105       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7106     else if (can_turn_right)
7107       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7108     else
7109       MovDir[x][y] = back_dir;
7110
7111     MovDelay[x][y] = 16 + 16 * RND(3);
7112   }
7113   else if (element == EL_PACMAN)
7114   {
7115     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7116     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7117
7118     if (can_turn_left && can_turn_right)
7119       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7120     else if (can_turn_left)
7121       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7122     else if (can_turn_right)
7123       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7124     else
7125       MovDir[x][y] = back_dir;
7126
7127     MovDelay[x][y] = 6 + RND(40);
7128   }
7129   else if (element == EL_PIG)
7130   {
7131     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7132     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7133     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7134     boolean should_turn_left, should_turn_right, should_move_on;
7135     int rnd_value = 24;
7136     int rnd = RND(rnd_value);
7137
7138     should_turn_left = (can_turn_left &&
7139                         (!can_move_on ||
7140                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7141                                                    y + back_dy + left_dy)));
7142     should_turn_right = (can_turn_right &&
7143                          (!can_move_on ||
7144                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7145                                                     y + back_dy + right_dy)));
7146     should_move_on = (can_move_on &&
7147                       (!can_turn_left ||
7148                        !can_turn_right ||
7149                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7150                                                  y + move_dy + left_dy) ||
7151                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7152                                                  y + move_dy + right_dy)));
7153
7154     if (should_turn_left || should_turn_right || should_move_on)
7155     {
7156       if (should_turn_left && should_turn_right && should_move_on)
7157         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7158                         rnd < 2 * rnd_value / 3 ? right_dir :
7159                         old_move_dir);
7160       else if (should_turn_left && should_turn_right)
7161         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7162       else if (should_turn_left && should_move_on)
7163         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7164       else if (should_turn_right && should_move_on)
7165         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7166       else if (should_turn_left)
7167         MovDir[x][y] = left_dir;
7168       else if (should_turn_right)
7169         MovDir[x][y] = right_dir;
7170       else if (should_move_on)
7171         MovDir[x][y] = old_move_dir;
7172     }
7173     else if (can_move_on && rnd > rnd_value / 8)
7174       MovDir[x][y] = old_move_dir;
7175     else if (can_turn_left && can_turn_right)
7176       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7177     else if (can_turn_left && rnd > rnd_value / 8)
7178       MovDir[x][y] = left_dir;
7179     else if (can_turn_right && rnd > rnd_value/8)
7180       MovDir[x][y] = right_dir;
7181     else
7182       MovDir[x][y] = back_dir;
7183
7184     xx = x + move_xy[MovDir[x][y]].dx;
7185     yy = y + move_xy[MovDir[x][y]].dy;
7186
7187     if (!IN_LEV_FIELD(xx, yy) ||
7188         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7189       MovDir[x][y] = old_move_dir;
7190
7191     MovDelay[x][y] = 0;
7192   }
7193   else if (element == EL_DRAGON)
7194   {
7195     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7196     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7197     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7198     int rnd_value = 24;
7199     int rnd = RND(rnd_value);
7200
7201     if (can_move_on && rnd > rnd_value / 8)
7202       MovDir[x][y] = old_move_dir;
7203     else if (can_turn_left && can_turn_right)
7204       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7205     else if (can_turn_left && rnd > rnd_value / 8)
7206       MovDir[x][y] = left_dir;
7207     else if (can_turn_right && rnd > rnd_value / 8)
7208       MovDir[x][y] = right_dir;
7209     else
7210       MovDir[x][y] = back_dir;
7211
7212     xx = x + move_xy[MovDir[x][y]].dx;
7213     yy = y + move_xy[MovDir[x][y]].dy;
7214
7215     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7216       MovDir[x][y] = old_move_dir;
7217
7218     MovDelay[x][y] = 0;
7219   }
7220   else if (element == EL_MOLE)
7221   {
7222     boolean can_move_on =
7223       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7224                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7225                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7226     if (!can_move_on)
7227     {
7228       boolean can_turn_left =
7229         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7230                               IS_AMOEBOID(Tile[left_x][left_y])));
7231
7232       boolean can_turn_right =
7233         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7234                               IS_AMOEBOID(Tile[right_x][right_y])));
7235
7236       if (can_turn_left && can_turn_right)
7237         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7238       else if (can_turn_left)
7239         MovDir[x][y] = left_dir;
7240       else
7241         MovDir[x][y] = right_dir;
7242     }
7243
7244     if (MovDir[x][y] != old_move_dir)
7245       MovDelay[x][y] = 9;
7246   }
7247   else if (element == EL_BALLOON)
7248   {
7249     MovDir[x][y] = game.wind_direction;
7250     MovDelay[x][y] = 0;
7251   }
7252   else if (element == EL_SPRING)
7253   {
7254     if (MovDir[x][y] & MV_HORIZONTAL)
7255     {
7256       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7257           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7258       {
7259         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7260         ResetGfxAnimation(move_x, move_y);
7261         TEST_DrawLevelField(move_x, move_y);
7262
7263         MovDir[x][y] = back_dir;
7264       }
7265       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7266                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7267         MovDir[x][y] = MV_NONE;
7268     }
7269
7270     MovDelay[x][y] = 0;
7271   }
7272   else if (element == EL_ROBOT ||
7273            element == EL_SATELLITE ||
7274            element == EL_PENGUIN ||
7275            element == EL_EMC_ANDROID)
7276   {
7277     int attr_x = -1, attr_y = -1;
7278
7279     if (game.all_players_gone)
7280     {
7281       attr_x = game.exit_x;
7282       attr_y = game.exit_y;
7283     }
7284     else
7285     {
7286       int i;
7287
7288       for (i = 0; i < MAX_PLAYERS; i++)
7289       {
7290         struct PlayerInfo *player = &stored_player[i];
7291         int jx = player->jx, jy = player->jy;
7292
7293         if (!player->active)
7294           continue;
7295
7296         if (attr_x == -1 ||
7297             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7298         {
7299           attr_x = jx;
7300           attr_y = jy;
7301         }
7302       }
7303     }
7304
7305     if (element == EL_ROBOT &&
7306         game.robot_wheel_x >= 0 &&
7307         game.robot_wheel_y >= 0 &&
7308         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7309          game.engine_version < VERSION_IDENT(3,1,0,0)))
7310     {
7311       attr_x = game.robot_wheel_x;
7312       attr_y = game.robot_wheel_y;
7313     }
7314
7315     if (element == EL_PENGUIN)
7316     {
7317       int i;
7318       struct XY *xy = xy_topdown;
7319
7320       for (i = 0; i < NUM_DIRECTIONS; i++)
7321       {
7322         int ex = x + xy[i].x;
7323         int ey = y + xy[i].y;
7324
7325         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7326                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7327                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7328                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7329         {
7330           attr_x = ex;
7331           attr_y = ey;
7332           break;
7333         }
7334       }
7335     }
7336
7337     MovDir[x][y] = MV_NONE;
7338     if (attr_x < x)
7339       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7340     else if (attr_x > x)
7341       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7342     if (attr_y < y)
7343       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7344     else if (attr_y > y)
7345       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7346
7347     if (element == EL_ROBOT)
7348     {
7349       int newx, newy;
7350
7351       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7352         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7353       Moving2Blocked(x, y, &newx, &newy);
7354
7355       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7356         MovDelay[x][y] = 8 + 8 * !RND(3);
7357       else
7358         MovDelay[x][y] = 16;
7359     }
7360     else if (element == EL_PENGUIN)
7361     {
7362       int newx, newy;
7363
7364       MovDelay[x][y] = 1;
7365
7366       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7367       {
7368         boolean first_horiz = RND(2);
7369         int new_move_dir = MovDir[x][y];
7370
7371         MovDir[x][y] =
7372           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7373         Moving2Blocked(x, y, &newx, &newy);
7374
7375         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7376           return;
7377
7378         MovDir[x][y] =
7379           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7380         Moving2Blocked(x, y, &newx, &newy);
7381
7382         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7383           return;
7384
7385         MovDir[x][y] = old_move_dir;
7386         return;
7387       }
7388     }
7389     else if (element == EL_SATELLITE)
7390     {
7391       int newx, newy;
7392
7393       MovDelay[x][y] = 1;
7394
7395       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7396       {
7397         boolean first_horiz = RND(2);
7398         int new_move_dir = MovDir[x][y];
7399
7400         MovDir[x][y] =
7401           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7402         Moving2Blocked(x, y, &newx, &newy);
7403
7404         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7405           return;
7406
7407         MovDir[x][y] =
7408           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7409         Moving2Blocked(x, y, &newx, &newy);
7410
7411         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7412           return;
7413
7414         MovDir[x][y] = old_move_dir;
7415         return;
7416       }
7417     }
7418     else if (element == EL_EMC_ANDROID)
7419     {
7420       static int check_pos[16] =
7421       {
7422         -1,             //  0 => (invalid)
7423         7,              //  1 => MV_LEFT
7424         3,              //  2 => MV_RIGHT
7425         -1,             //  3 => (invalid)
7426         1,              //  4 =>            MV_UP
7427         0,              //  5 => MV_LEFT  | MV_UP
7428         2,              //  6 => MV_RIGHT | MV_UP
7429         -1,             //  7 => (invalid)
7430         5,              //  8 =>            MV_DOWN
7431         6,              //  9 => MV_LEFT  | MV_DOWN
7432         4,              // 10 => MV_RIGHT | MV_DOWN
7433         -1,             // 11 => (invalid)
7434         -1,             // 12 => (invalid)
7435         -1,             // 13 => (invalid)
7436         -1,             // 14 => (invalid)
7437         -1,             // 15 => (invalid)
7438       };
7439       static struct
7440       {
7441         int dx, dy;
7442         int dir;
7443       } check_xy[8] =
7444       {
7445         { -1, -1,       MV_LEFT  | MV_UP   },
7446         {  0, -1,                  MV_UP   },
7447         { +1, -1,       MV_RIGHT | MV_UP   },
7448         { +1,  0,       MV_RIGHT           },
7449         { +1, +1,       MV_RIGHT | MV_DOWN },
7450         {  0, +1,                  MV_DOWN },
7451         { -1, +1,       MV_LEFT  | MV_DOWN },
7452         { -1,  0,       MV_LEFT            },
7453       };
7454       int start_pos, check_order;
7455       boolean can_clone = FALSE;
7456       int i;
7457
7458       // check if there is any free field around current position
7459       for (i = 0; i < 8; i++)
7460       {
7461         int newx = x + check_xy[i].dx;
7462         int newy = y + check_xy[i].dy;
7463
7464         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7465         {
7466           can_clone = TRUE;
7467
7468           break;
7469         }
7470       }
7471
7472       if (can_clone)            // randomly find an element to clone
7473       {
7474         can_clone = FALSE;
7475
7476         start_pos = check_pos[RND(8)];
7477         check_order = (RND(2) ? -1 : +1);
7478
7479         for (i = 0; i < 8; i++)
7480         {
7481           int pos_raw = start_pos + i * check_order;
7482           int pos = (pos_raw + 8) % 8;
7483           int newx = x + check_xy[pos].dx;
7484           int newy = y + check_xy[pos].dy;
7485
7486           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7487           {
7488             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7489             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7490
7491             Store[x][y] = Tile[newx][newy];
7492
7493             can_clone = TRUE;
7494
7495             break;
7496           }
7497         }
7498       }
7499
7500       if (can_clone)            // randomly find a direction to move
7501       {
7502         can_clone = FALSE;
7503
7504         start_pos = check_pos[RND(8)];
7505         check_order = (RND(2) ? -1 : +1);
7506
7507         for (i = 0; i < 8; i++)
7508         {
7509           int pos_raw = start_pos + i * check_order;
7510           int pos = (pos_raw + 8) % 8;
7511           int newx = x + check_xy[pos].dx;
7512           int newy = y + check_xy[pos].dy;
7513           int new_move_dir = check_xy[pos].dir;
7514
7515           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7516           {
7517             MovDir[x][y] = new_move_dir;
7518             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7519
7520             can_clone = TRUE;
7521
7522             break;
7523           }
7524         }
7525       }
7526
7527       if (can_clone)            // cloning and moving successful
7528         return;
7529
7530       // cannot clone -- try to move towards player
7531
7532       start_pos = check_pos[MovDir[x][y] & 0x0f];
7533       check_order = (RND(2) ? -1 : +1);
7534
7535       for (i = 0; i < 3; i++)
7536       {
7537         // first check start_pos, then previous/next or (next/previous) pos
7538         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7539         int pos = (pos_raw + 8) % 8;
7540         int newx = x + check_xy[pos].dx;
7541         int newy = y + check_xy[pos].dy;
7542         int new_move_dir = check_xy[pos].dir;
7543
7544         if (IS_PLAYER(newx, newy))
7545           break;
7546
7547         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7548         {
7549           MovDir[x][y] = new_move_dir;
7550           MovDelay[x][y] = level.android_move_time * 8 + 1;
7551
7552           break;
7553         }
7554       }
7555     }
7556   }
7557   else if (move_pattern == MV_TURNING_LEFT ||
7558            move_pattern == MV_TURNING_RIGHT ||
7559            move_pattern == MV_TURNING_LEFT_RIGHT ||
7560            move_pattern == MV_TURNING_RIGHT_LEFT ||
7561            move_pattern == MV_TURNING_RANDOM ||
7562            move_pattern == MV_ALL_DIRECTIONS)
7563   {
7564     boolean can_turn_left =
7565       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7566     boolean can_turn_right =
7567       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y);
7568
7569     if (element_info[element].move_stepsize == 0)       // "not moving"
7570       return;
7571
7572     if (move_pattern == MV_TURNING_LEFT)
7573       MovDir[x][y] = left_dir;
7574     else if (move_pattern == MV_TURNING_RIGHT)
7575       MovDir[x][y] = right_dir;
7576     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7577       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7578     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7579       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7580     else if (move_pattern == MV_TURNING_RANDOM)
7581       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7582                       can_turn_right && !can_turn_left ? right_dir :
7583                       RND(2) ? left_dir : right_dir);
7584     else if (can_turn_left && can_turn_right)
7585       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7586     else if (can_turn_left)
7587       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7588     else if (can_turn_right)
7589       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7590     else
7591       MovDir[x][y] = back_dir;
7592
7593     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7594   }
7595   else if (move_pattern == MV_HORIZONTAL ||
7596            move_pattern == MV_VERTICAL)
7597   {
7598     if (move_pattern & old_move_dir)
7599       MovDir[x][y] = back_dir;
7600     else if (move_pattern == MV_HORIZONTAL)
7601       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7602     else if (move_pattern == MV_VERTICAL)
7603       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7604
7605     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7606   }
7607   else if (move_pattern & MV_ANY_DIRECTION)
7608   {
7609     MovDir[x][y] = move_pattern;
7610     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7611   }
7612   else if (move_pattern & MV_WIND_DIRECTION)
7613   {
7614     MovDir[x][y] = game.wind_direction;
7615     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7616   }
7617   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7618   {
7619     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7620       MovDir[x][y] = left_dir;
7621     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7622       MovDir[x][y] = right_dir;
7623
7624     if (MovDir[x][y] != old_move_dir)
7625       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7626   }
7627   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7628   {
7629     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7630       MovDir[x][y] = right_dir;
7631     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7632       MovDir[x][y] = left_dir;
7633
7634     if (MovDir[x][y] != old_move_dir)
7635       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7636   }
7637   else if (move_pattern == MV_TOWARDS_PLAYER ||
7638            move_pattern == MV_AWAY_FROM_PLAYER)
7639   {
7640     int attr_x = -1, attr_y = -1;
7641     int newx, newy;
7642     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7643
7644     if (game.all_players_gone)
7645     {
7646       attr_x = game.exit_x;
7647       attr_y = game.exit_y;
7648     }
7649     else
7650     {
7651       int i;
7652
7653       for (i = 0; i < MAX_PLAYERS; i++)
7654       {
7655         struct PlayerInfo *player = &stored_player[i];
7656         int jx = player->jx, jy = player->jy;
7657
7658         if (!player->active)
7659           continue;
7660
7661         if (attr_x == -1 ||
7662             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7663         {
7664           attr_x = jx;
7665           attr_y = jy;
7666         }
7667       }
7668     }
7669
7670     MovDir[x][y] = MV_NONE;
7671     if (attr_x < x)
7672       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7673     else if (attr_x > x)
7674       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7675     if (attr_y < y)
7676       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7677     else if (attr_y > y)
7678       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7679
7680     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7681
7682     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7683     {
7684       boolean first_horiz = RND(2);
7685       int new_move_dir = MovDir[x][y];
7686
7687       if (element_info[element].move_stepsize == 0)     // "not moving"
7688       {
7689         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7690         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7691
7692         return;
7693       }
7694
7695       MovDir[x][y] =
7696         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7697       Moving2Blocked(x, y, &newx, &newy);
7698
7699       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7700         return;
7701
7702       MovDir[x][y] =
7703         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7704       Moving2Blocked(x, y, &newx, &newy);
7705
7706       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7707         return;
7708
7709       MovDir[x][y] = old_move_dir;
7710     }
7711   }
7712   else if (move_pattern == MV_WHEN_PUSHED ||
7713            move_pattern == MV_WHEN_DROPPED)
7714   {
7715     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7716       MovDir[x][y] = MV_NONE;
7717
7718     MovDelay[x][y] = 0;
7719   }
7720   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7721   {
7722     struct XY *test_xy = xy_topdown;
7723     static int test_dir[4] =
7724     {
7725       MV_UP,
7726       MV_LEFT,
7727       MV_RIGHT,
7728       MV_DOWN
7729     };
7730     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7731     int move_preference = -1000000;     // start with very low preference
7732     int new_move_dir = MV_NONE;
7733     int start_test = RND(4);
7734     int i;
7735
7736     for (i = 0; i < NUM_DIRECTIONS; i++)
7737     {
7738       int j = (start_test + i) % 4;
7739       int move_dir = test_dir[j];
7740       int move_dir_preference;
7741
7742       xx = x + test_xy[j].x;
7743       yy = y + test_xy[j].y;
7744
7745       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7746           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7747       {
7748         new_move_dir = move_dir;
7749
7750         break;
7751       }
7752
7753       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7754         continue;
7755
7756       move_dir_preference = -1 * RunnerVisit[xx][yy];
7757       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7758         move_dir_preference = PlayerVisit[xx][yy];
7759
7760       if (move_dir_preference > move_preference)
7761       {
7762         // prefer field that has not been visited for the longest time
7763         move_preference = move_dir_preference;
7764         new_move_dir = move_dir;
7765       }
7766       else if (move_dir_preference == move_preference &&
7767                move_dir == old_move_dir)
7768       {
7769         // prefer last direction when all directions are preferred equally
7770         move_preference = move_dir_preference;
7771         new_move_dir = move_dir;
7772       }
7773     }
7774
7775     MovDir[x][y] = new_move_dir;
7776     if (old_move_dir != new_move_dir)
7777       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7778   }
7779 }
7780
7781 static void TurnRound(int x, int y)
7782 {
7783   int direction = MovDir[x][y];
7784
7785   TurnRoundExt(x, y);
7786
7787   GfxDir[x][y] = MovDir[x][y];
7788
7789   if (direction != MovDir[x][y])
7790     GfxFrame[x][y] = 0;
7791
7792   if (MovDelay[x][y])
7793     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7794
7795   ResetGfxFrame(x, y);
7796 }
7797
7798 static boolean JustBeingPushed(int x, int y)
7799 {
7800   int i;
7801
7802   for (i = 0; i < MAX_PLAYERS; i++)
7803   {
7804     struct PlayerInfo *player = &stored_player[i];
7805
7806     if (player->active && player->is_pushing && player->MovPos)
7807     {
7808       int next_jx = player->jx + (player->jx - player->last_jx);
7809       int next_jy = player->jy + (player->jy - player->last_jy);
7810
7811       if (x == next_jx && y == next_jy)
7812         return TRUE;
7813     }
7814   }
7815
7816   return FALSE;
7817 }
7818
7819 static void StartMoving(int x, int y)
7820 {
7821   boolean started_moving = FALSE;       // some elements can fall _and_ move
7822   int element = Tile[x][y];
7823
7824   if (Stop[x][y])
7825     return;
7826
7827   if (MovDelay[x][y] == 0)
7828     GfxAction[x][y] = ACTION_DEFAULT;
7829
7830   if (CAN_FALL(element) && y < lev_fieldy - 1)
7831   {
7832     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7833         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7834       if (JustBeingPushed(x, y))
7835         return;
7836
7837     if (element == EL_QUICKSAND_FULL)
7838     {
7839       if (IS_FREE(x, y + 1))
7840       {
7841         InitMovingField(x, y, MV_DOWN);
7842         started_moving = TRUE;
7843
7844         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7845 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7846         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7847           Store[x][y] = EL_ROCK;
7848 #else
7849         Store[x][y] = EL_ROCK;
7850 #endif
7851
7852         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7853       }
7854       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7855       {
7856         if (!MovDelay[x][y])
7857         {
7858           MovDelay[x][y] = TILEY + 1;
7859
7860           ResetGfxAnimation(x, y);
7861           ResetGfxAnimation(x, y + 1);
7862         }
7863
7864         if (MovDelay[x][y])
7865         {
7866           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7867           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7868
7869           MovDelay[x][y]--;
7870           if (MovDelay[x][y])
7871             return;
7872         }
7873
7874         Tile[x][y] = EL_QUICKSAND_EMPTY;
7875         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7876         Store[x][y + 1] = Store[x][y];
7877         Store[x][y] = 0;
7878
7879         PlayLevelSoundAction(x, y, ACTION_FILLING);
7880       }
7881       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7882       {
7883         if (!MovDelay[x][y])
7884         {
7885           MovDelay[x][y] = TILEY + 1;
7886
7887           ResetGfxAnimation(x, y);
7888           ResetGfxAnimation(x, y + 1);
7889         }
7890
7891         if (MovDelay[x][y])
7892         {
7893           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7894           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7895
7896           MovDelay[x][y]--;
7897           if (MovDelay[x][y])
7898             return;
7899         }
7900
7901         Tile[x][y] = EL_QUICKSAND_EMPTY;
7902         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7903         Store[x][y + 1] = Store[x][y];
7904         Store[x][y] = 0;
7905
7906         PlayLevelSoundAction(x, y, ACTION_FILLING);
7907       }
7908     }
7909     else if (element == EL_QUICKSAND_FAST_FULL)
7910     {
7911       if (IS_FREE(x, y + 1))
7912       {
7913         InitMovingField(x, y, MV_DOWN);
7914         started_moving = TRUE;
7915
7916         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7917 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7918         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7919           Store[x][y] = EL_ROCK;
7920 #else
7921         Store[x][y] = EL_ROCK;
7922 #endif
7923
7924         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7925       }
7926       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7927       {
7928         if (!MovDelay[x][y])
7929         {
7930           MovDelay[x][y] = TILEY + 1;
7931
7932           ResetGfxAnimation(x, y);
7933           ResetGfxAnimation(x, y + 1);
7934         }
7935
7936         if (MovDelay[x][y])
7937         {
7938           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7939           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7940
7941           MovDelay[x][y]--;
7942           if (MovDelay[x][y])
7943             return;
7944         }
7945
7946         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7947         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7948         Store[x][y + 1] = Store[x][y];
7949         Store[x][y] = 0;
7950
7951         PlayLevelSoundAction(x, y, ACTION_FILLING);
7952       }
7953       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7954       {
7955         if (!MovDelay[x][y])
7956         {
7957           MovDelay[x][y] = TILEY + 1;
7958
7959           ResetGfxAnimation(x, y);
7960           ResetGfxAnimation(x, y + 1);
7961         }
7962
7963         if (MovDelay[x][y])
7964         {
7965           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7966           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7967
7968           MovDelay[x][y]--;
7969           if (MovDelay[x][y])
7970             return;
7971         }
7972
7973         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7974         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7975         Store[x][y + 1] = Store[x][y];
7976         Store[x][y] = 0;
7977
7978         PlayLevelSoundAction(x, y, ACTION_FILLING);
7979       }
7980     }
7981     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7982              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7983     {
7984       InitMovingField(x, y, MV_DOWN);
7985       started_moving = TRUE;
7986
7987       Tile[x][y] = EL_QUICKSAND_FILLING;
7988       Store[x][y] = element;
7989
7990       PlayLevelSoundAction(x, y, ACTION_FILLING);
7991     }
7992     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7993              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7994     {
7995       InitMovingField(x, y, MV_DOWN);
7996       started_moving = TRUE;
7997
7998       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7999       Store[x][y] = element;
8000
8001       PlayLevelSoundAction(x, y, ACTION_FILLING);
8002     }
8003     else if (element == EL_MAGIC_WALL_FULL)
8004     {
8005       if (IS_FREE(x, y + 1))
8006       {
8007         InitMovingField(x, y, MV_DOWN);
8008         started_moving = TRUE;
8009
8010         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
8011         Store[x][y] = EL_CHANGED(Store[x][y]);
8012       }
8013       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8014       {
8015         if (!MovDelay[x][y])
8016           MovDelay[x][y] = TILEY / 4 + 1;
8017
8018         if (MovDelay[x][y])
8019         {
8020           MovDelay[x][y]--;
8021           if (MovDelay[x][y])
8022             return;
8023         }
8024
8025         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
8026         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
8027         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8028         Store[x][y] = 0;
8029       }
8030     }
8031     else if (element == EL_BD_MAGIC_WALL_FULL)
8032     {
8033       if (IS_FREE(x, y + 1))
8034       {
8035         InitMovingField(x, y, MV_DOWN);
8036         started_moving = TRUE;
8037
8038         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8039         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8040       }
8041       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8042       {
8043         if (!MovDelay[x][y])
8044           MovDelay[x][y] = TILEY / 4 + 1;
8045
8046         if (MovDelay[x][y])
8047         {
8048           MovDelay[x][y]--;
8049           if (MovDelay[x][y])
8050             return;
8051         }
8052
8053         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8054         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8055         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8056         Store[x][y] = 0;
8057       }
8058     }
8059     else if (element == EL_DC_MAGIC_WALL_FULL)
8060     {
8061       if (IS_FREE(x, y + 1))
8062       {
8063         InitMovingField(x, y, MV_DOWN);
8064         started_moving = TRUE;
8065
8066         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8067         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8068       }
8069       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8070       {
8071         if (!MovDelay[x][y])
8072           MovDelay[x][y] = TILEY / 4 + 1;
8073
8074         if (MovDelay[x][y])
8075         {
8076           MovDelay[x][y]--;
8077           if (MovDelay[x][y])
8078             return;
8079         }
8080
8081         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8082         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8083         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8084         Store[x][y] = 0;
8085       }
8086     }
8087     else if ((CAN_PASS_MAGIC_WALL(element) &&
8088               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8089                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8090              (CAN_PASS_DC_MAGIC_WALL(element) &&
8091               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8092
8093     {
8094       InitMovingField(x, y, MV_DOWN);
8095       started_moving = TRUE;
8096
8097       Tile[x][y] =
8098         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8099          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8100          EL_DC_MAGIC_WALL_FILLING);
8101       Store[x][y] = element;
8102     }
8103     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
8104     {
8105       SplashAcid(x, y + 1);
8106
8107       InitMovingField(x, y, MV_DOWN);
8108       started_moving = TRUE;
8109
8110       Store[x][y] = EL_ACID;
8111     }
8112     else if (
8113              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8114               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8115              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8116               CAN_FALL(element) && WasJustFalling[x][y] &&
8117               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8118
8119              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8120               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8121               (Tile[x][y + 1] == EL_BLOCKED)))
8122     {
8123       /* this is needed for a special case not covered by calling "Impact()"
8124          from "ContinueMoving()": if an element moves to a tile directly below
8125          another element which was just falling on that tile (which was empty
8126          in the previous frame), the falling element above would just stop
8127          instead of smashing the element below (in previous version, the above
8128          element was just checked for "moving" instead of "falling", resulting
8129          in incorrect smashes caused by horizontal movement of the above
8130          element; also, the case of the player being the element to smash was
8131          simply not covered here... :-/ ) */
8132
8133       CheckCollision[x][y] = 0;
8134       CheckImpact[x][y] = 0;
8135
8136       Impact(x, y);
8137     }
8138     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8139     {
8140       if (MovDir[x][y] == MV_NONE)
8141       {
8142         InitMovingField(x, y, MV_DOWN);
8143         started_moving = TRUE;
8144       }
8145     }
8146     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8147     {
8148       if (WasJustFalling[x][y]) // prevent animation from being restarted
8149         MovDir[x][y] = MV_DOWN;
8150
8151       InitMovingField(x, y, MV_DOWN);
8152       started_moving = TRUE;
8153     }
8154     else if (element == EL_AMOEBA_DROP)
8155     {
8156       Tile[x][y] = EL_AMOEBA_GROWING;
8157       Store[x][y] = EL_AMOEBA_WET;
8158     }
8159     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8160               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8161              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8162              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8163     {
8164       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8165                                 (IS_FREE(x - 1, y + 1) ||
8166                                  Tile[x - 1][y + 1] == EL_ACID));
8167       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8168                                 (IS_FREE(x + 1, y + 1) ||
8169                                  Tile[x + 1][y + 1] == EL_ACID));
8170       boolean can_fall_any  = (can_fall_left || can_fall_right);
8171       boolean can_fall_both = (can_fall_left && can_fall_right);
8172       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8173
8174       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8175       {
8176         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8177           can_fall_right = FALSE;
8178         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8179           can_fall_left = FALSE;
8180         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8181           can_fall_right = FALSE;
8182         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8183           can_fall_left = FALSE;
8184
8185         can_fall_any  = (can_fall_left || can_fall_right);
8186         can_fall_both = FALSE;
8187       }
8188
8189       if (can_fall_both)
8190       {
8191         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8192           can_fall_right = FALSE;       // slip down on left side
8193         else
8194           can_fall_left = !(can_fall_right = RND(2));
8195
8196         can_fall_both = FALSE;
8197       }
8198
8199       if (can_fall_any)
8200       {
8201         // if not determined otherwise, prefer left side for slipping down
8202         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8203         started_moving = TRUE;
8204       }
8205     }
8206     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8207     {
8208       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8209       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8210       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8211       int belt_dir = game.belt_dir[belt_nr];
8212
8213       if ((belt_dir == MV_LEFT  && left_is_free) ||
8214           (belt_dir == MV_RIGHT && right_is_free))
8215       {
8216         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8217
8218         InitMovingField(x, y, belt_dir);
8219         started_moving = TRUE;
8220
8221         Pushed[x][y] = TRUE;
8222         Pushed[nextx][y] = TRUE;
8223
8224         GfxAction[x][y] = ACTION_DEFAULT;
8225       }
8226       else
8227       {
8228         MovDir[x][y] = 0;       // if element was moving, stop it
8229       }
8230     }
8231   }
8232
8233   // not "else if" because of elements that can fall and move (EL_SPRING)
8234   if (CAN_MOVE(element) && !started_moving)
8235   {
8236     int move_pattern = element_info[element].move_pattern;
8237     int newx, newy;
8238
8239     Moving2Blocked(x, y, &newx, &newy);
8240
8241     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8242       return;
8243
8244     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8245         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8246     {
8247       WasJustMoving[x][y] = 0;
8248       CheckCollision[x][y] = 0;
8249
8250       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8251
8252       if (Tile[x][y] != element)        // element has changed
8253         return;
8254     }
8255
8256     if (!MovDelay[x][y])        // start new movement phase
8257     {
8258       // all objects that can change their move direction after each step
8259       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8260
8261       if (element != EL_YAMYAM &&
8262           element != EL_DARK_YAMYAM &&
8263           element != EL_PACMAN &&
8264           !(move_pattern & MV_ANY_DIRECTION) &&
8265           move_pattern != MV_TURNING_LEFT &&
8266           move_pattern != MV_TURNING_RIGHT &&
8267           move_pattern != MV_TURNING_LEFT_RIGHT &&
8268           move_pattern != MV_TURNING_RIGHT_LEFT &&
8269           move_pattern != MV_TURNING_RANDOM)
8270       {
8271         TurnRound(x, y);
8272
8273         if (MovDelay[x][y] && (element == EL_BUG ||
8274                                element == EL_SPACESHIP ||
8275                                element == EL_SP_SNIKSNAK ||
8276                                element == EL_SP_ELECTRON ||
8277                                element == EL_MOLE))
8278           TEST_DrawLevelField(x, y);
8279       }
8280     }
8281
8282     if (MovDelay[x][y])         // wait some time before next movement
8283     {
8284       MovDelay[x][y]--;
8285
8286       if (element == EL_ROBOT ||
8287           element == EL_YAMYAM ||
8288           element == EL_DARK_YAMYAM)
8289       {
8290         DrawLevelElementAnimationIfNeeded(x, y, element);
8291         PlayLevelSoundAction(x, y, ACTION_WAITING);
8292       }
8293       else if (element == EL_SP_ELECTRON)
8294         DrawLevelElementAnimationIfNeeded(x, y, element);
8295       else if (element == EL_DRAGON)
8296       {
8297         int i;
8298         int dir = MovDir[x][y];
8299         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8300         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8301         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8302                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8303                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8304                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8305         int frame = getGraphicAnimationFrameXY(graphic, x, y);
8306
8307         GfxAction[x][y] = ACTION_ATTACKING;
8308
8309         if (IS_PLAYER(x, y))
8310           DrawPlayerField(x, y);
8311         else
8312           TEST_DrawLevelField(x, y);
8313
8314         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8315
8316         for (i = 1; i <= 3; i++)
8317         {
8318           int xx = x + i * dx;
8319           int yy = y + i * dy;
8320           int sx = SCREENX(xx);
8321           int sy = SCREENY(yy);
8322           int flame_graphic = graphic + (i - 1);
8323
8324           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8325             break;
8326
8327           if (MovDelay[x][y])
8328           {
8329             int flamed = MovingOrBlocked2Element(xx, yy);
8330
8331             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8332               Bang(xx, yy);
8333             else
8334               RemoveMovingField(xx, yy);
8335
8336             ChangeDelay[xx][yy] = 0;
8337
8338             Tile[xx][yy] = EL_FLAMES;
8339
8340             if (IN_SCR_FIELD(sx, sy))
8341             {
8342               TEST_DrawLevelFieldCrumbled(xx, yy);
8343               DrawScreenGraphic(sx, sy, flame_graphic, frame);
8344             }
8345           }
8346           else
8347           {
8348             if (Tile[xx][yy] == EL_FLAMES)
8349               Tile[xx][yy] = EL_EMPTY;
8350             TEST_DrawLevelField(xx, yy);
8351           }
8352         }
8353       }
8354
8355       if (MovDelay[x][y])       // element still has to wait some time
8356       {
8357         PlayLevelSoundAction(x, y, ACTION_WAITING);
8358
8359         return;
8360       }
8361     }
8362
8363     // now make next step
8364
8365     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8366
8367     if (DONT_COLLIDE_WITH(element) &&
8368         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8369         !PLAYER_ENEMY_PROTECTED(newx, newy))
8370     {
8371       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8372
8373       return;
8374     }
8375
8376     else if (CAN_MOVE_INTO_ACID(element) &&
8377              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8378              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8379              (MovDir[x][y] == MV_DOWN ||
8380               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8381     {
8382       SplashAcid(newx, newy);
8383       Store[x][y] = EL_ACID;
8384     }
8385     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8386     {
8387       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8388           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8389           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8390           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8391       {
8392         RemoveField(x, y);
8393         TEST_DrawLevelField(x, y);
8394
8395         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8396         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8397           DrawGraphicThruMask(SCREENX(newx), SCREENY(newy), el2img(element), 0);
8398
8399         game.friends_still_needed--;
8400         if (!game.friends_still_needed &&
8401             !game.GameOver &&
8402             game.all_players_gone)
8403           LevelSolved();
8404
8405         return;
8406       }
8407       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8408       {
8409         if (DigField(local_player, x, y, newx, newy, 0, 0, DF_DIG) == MP_MOVING)
8410           TEST_DrawLevelField(newx, newy);
8411         else
8412           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8413       }
8414       else if (!IS_FREE(newx, newy))
8415       {
8416         GfxAction[x][y] = ACTION_WAITING;
8417
8418         if (IS_PLAYER(x, y))
8419           DrawPlayerField(x, y);
8420         else
8421           TEST_DrawLevelField(x, y);
8422
8423         return;
8424       }
8425     }
8426     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8427     {
8428       if (IS_FOOD_PIG(Tile[newx][newy]))
8429       {
8430         if (IS_MOVING(newx, newy))
8431           RemoveMovingField(newx, newy);
8432         else
8433         {
8434           Tile[newx][newy] = EL_EMPTY;
8435           TEST_DrawLevelField(newx, newy);
8436         }
8437
8438         PlayLevelSound(x, y, SND_PIG_DIGGING);
8439       }
8440       else if (!IS_FREE(newx, newy))
8441       {
8442         if (IS_PLAYER(x, y))
8443           DrawPlayerField(x, y);
8444         else
8445           TEST_DrawLevelField(x, y);
8446
8447         return;
8448       }
8449     }
8450     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8451     {
8452       if (Store[x][y] != EL_EMPTY)
8453       {
8454         boolean can_clone = FALSE;
8455         int xx, yy;
8456
8457         // check if element to clone is still there
8458         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8459         {
8460           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8461           {
8462             can_clone = TRUE;
8463
8464             break;
8465           }
8466         }
8467
8468         // cannot clone or target field not free anymore -- do not clone
8469         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8470           Store[x][y] = EL_EMPTY;
8471       }
8472
8473       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8474       {
8475         if (IS_MV_DIAGONAL(MovDir[x][y]))
8476         {
8477           int diagonal_move_dir = MovDir[x][y];
8478           int stored = Store[x][y];
8479           int change_delay = 8;
8480           int graphic;
8481
8482           // android is moving diagonally
8483
8484           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8485
8486           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8487           GfxElement[x][y] = EL_EMC_ANDROID;
8488           GfxAction[x][y] = ACTION_SHRINKING;
8489           GfxDir[x][y] = diagonal_move_dir;
8490           ChangeDelay[x][y] = change_delay;
8491
8492           if (Store[x][y] == EL_EMPTY)
8493             Store[x][y] = GfxElementEmpty[x][y];
8494
8495           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8496                                    GfxDir[x][y]);
8497
8498           DrawLevelGraphicAnimation(x, y, graphic);
8499           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8500
8501           if (Tile[newx][newy] == EL_ACID)
8502           {
8503             SplashAcid(newx, newy);
8504
8505             return;
8506           }
8507
8508           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8509
8510           Store[newx][newy] = EL_EMC_ANDROID;
8511           GfxElement[newx][newy] = EL_EMC_ANDROID;
8512           GfxAction[newx][newy] = ACTION_GROWING;
8513           GfxDir[newx][newy] = diagonal_move_dir;
8514           ChangeDelay[newx][newy] = change_delay;
8515
8516           graphic = el_act_dir2img(GfxElement[newx][newy],
8517                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8518
8519           DrawLevelGraphicAnimation(newx, newy, graphic);
8520           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8521
8522           return;
8523         }
8524         else
8525         {
8526           Tile[newx][newy] = EL_EMPTY;
8527           TEST_DrawLevelField(newx, newy);
8528
8529           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8530         }
8531       }
8532       else if (!IS_FREE(newx, newy))
8533       {
8534         return;
8535       }
8536     }
8537     else if (IS_CUSTOM_ELEMENT(element) &&
8538              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8539     {
8540       if (!DigFieldByCE(newx, newy, element))
8541         return;
8542
8543       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8544       {
8545         RunnerVisit[x][y] = FrameCounter;
8546         PlayerVisit[x][y] /= 8;         // expire player visit path
8547       }
8548     }
8549     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8550     {
8551       if (!IS_FREE(newx, newy))
8552       {
8553         if (IS_PLAYER(x, y))
8554           DrawPlayerField(x, y);
8555         else
8556           TEST_DrawLevelField(x, y);
8557
8558         return;
8559       }
8560       else
8561       {
8562         boolean wanna_flame = !RND(10);
8563         int dx = newx - x, dy = newy - y;
8564         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8565         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8566         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8567                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8568         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8569                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8570
8571         if ((wanna_flame ||
8572              IS_CLASSIC_ENEMY(element1) ||
8573              IS_CLASSIC_ENEMY(element2)) &&
8574             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8575             element1 != EL_FLAMES && element2 != EL_FLAMES)
8576         {
8577           ResetGfxAnimation(x, y);
8578           GfxAction[x][y] = ACTION_ATTACKING;
8579
8580           if (IS_PLAYER(x, y))
8581             DrawPlayerField(x, y);
8582           else
8583             TEST_DrawLevelField(x, y);
8584
8585           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8586
8587           MovDelay[x][y] = 50;
8588
8589           Tile[newx][newy] = EL_FLAMES;
8590           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8591             Tile[newx1][newy1] = EL_FLAMES;
8592           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8593             Tile[newx2][newy2] = EL_FLAMES;
8594
8595           return;
8596         }
8597       }
8598     }
8599     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8600              Tile[newx][newy] == EL_DIAMOND)
8601     {
8602       if (IS_MOVING(newx, newy))
8603         RemoveMovingField(newx, newy);
8604       else
8605       {
8606         Tile[newx][newy] = EL_EMPTY;
8607         TEST_DrawLevelField(newx, newy);
8608       }
8609
8610       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8611     }
8612     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8613              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8614     {
8615       if (AmoebaNr[newx][newy])
8616       {
8617         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8618         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8619             Tile[newx][newy] == EL_BD_AMOEBA)
8620           AmoebaCnt[AmoebaNr[newx][newy]]--;
8621       }
8622
8623       if (IS_MOVING(newx, newy))
8624       {
8625         RemoveMovingField(newx, newy);
8626       }
8627       else
8628       {
8629         Tile[newx][newy] = EL_EMPTY;
8630         TEST_DrawLevelField(newx, newy);
8631       }
8632
8633       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8634     }
8635     else if ((element == EL_PACMAN || element == EL_MOLE)
8636              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8637     {
8638       if (AmoebaNr[newx][newy])
8639       {
8640         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8641         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8642             Tile[newx][newy] == EL_BD_AMOEBA)
8643           AmoebaCnt[AmoebaNr[newx][newy]]--;
8644       }
8645
8646       if (element == EL_MOLE)
8647       {
8648         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8649         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8650
8651         ResetGfxAnimation(x, y);
8652         GfxAction[x][y] = ACTION_DIGGING;
8653         TEST_DrawLevelField(x, y);
8654
8655         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8656
8657         return;                         // wait for shrinking amoeba
8658       }
8659       else      // element == EL_PACMAN
8660       {
8661         Tile[newx][newy] = EL_EMPTY;
8662         TEST_DrawLevelField(newx, newy);
8663         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8664       }
8665     }
8666     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8667              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8668               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8669     {
8670       // wait for shrinking amoeba to completely disappear
8671       return;
8672     }
8673     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8674     {
8675       // object was running against a wall
8676
8677       TurnRound(x, y);
8678
8679       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8680         DrawLevelElementAnimation(x, y, element);
8681
8682       if (DONT_TOUCH(element))
8683         TestIfBadThingTouchesPlayer(x, y);
8684
8685       return;
8686     }
8687
8688     InitMovingField(x, y, MovDir[x][y]);
8689
8690     PlayLevelSoundAction(x, y, ACTION_MOVING);
8691   }
8692
8693   if (MovDir[x][y])
8694     ContinueMoving(x, y);
8695 }
8696
8697 void ContinueMoving(int x, int y)
8698 {
8699   int element = Tile[x][y];
8700   struct ElementInfo *ei = &element_info[element];
8701   int direction = MovDir[x][y];
8702   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8703   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8704   int newx = x + dx, newy = y + dy;
8705   int stored = Store[x][y];
8706   int stored_new = Store[newx][newy];
8707   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8708   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8709   boolean last_line = (newy == lev_fieldy - 1);
8710   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8711
8712   if (pushed_by_player)         // special case: moving object pushed by player
8713   {
8714     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x, y)->MovPos));
8715   }
8716   else if (use_step_delay)      // special case: moving object has step delay
8717   {
8718     if (!MovDelay[x][y])
8719       MovPos[x][y] += getElementMoveStepsize(x, y);
8720
8721     if (MovDelay[x][y])
8722       MovDelay[x][y]--;
8723     else
8724       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8725
8726     if (MovDelay[x][y])
8727     {
8728       TEST_DrawLevelField(x, y);
8729
8730       return;   // element is still waiting
8731     }
8732   }
8733   else                          // normal case: generically moving object
8734   {
8735     MovPos[x][y] += getElementMoveStepsize(x, y);
8736   }
8737
8738   if (ABS(MovPos[x][y]) < TILEX)
8739   {
8740     TEST_DrawLevelField(x, y);
8741
8742     return;     // element is still moving
8743   }
8744
8745   // element reached destination field
8746
8747   Tile[x][y] = EL_EMPTY;
8748   Tile[newx][newy] = element;
8749   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8750
8751   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8752   {
8753     element = Tile[newx][newy] = EL_ACID;
8754   }
8755   else if (element == EL_MOLE)
8756   {
8757     Tile[x][y] = EL_SAND;
8758
8759     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8760   }
8761   else if (element == EL_QUICKSAND_FILLING)
8762   {
8763     element = Tile[newx][newy] = get_next_element(element);
8764     Store[newx][newy] = Store[x][y];
8765   }
8766   else if (element == EL_QUICKSAND_EMPTYING)
8767   {
8768     Tile[x][y] = get_next_element(element);
8769     element = Tile[newx][newy] = Store[x][y];
8770   }
8771   else if (element == EL_QUICKSAND_FAST_FILLING)
8772   {
8773     element = Tile[newx][newy] = get_next_element(element);
8774     Store[newx][newy] = Store[x][y];
8775   }
8776   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8777   {
8778     Tile[x][y] = get_next_element(element);
8779     element = Tile[newx][newy] = Store[x][y];
8780   }
8781   else if (element == EL_MAGIC_WALL_FILLING)
8782   {
8783     element = Tile[newx][newy] = get_next_element(element);
8784     if (!game.magic_wall_active)
8785       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8786     Store[newx][newy] = Store[x][y];
8787   }
8788   else if (element == EL_MAGIC_WALL_EMPTYING)
8789   {
8790     Tile[x][y] = get_next_element(element);
8791     if (!game.magic_wall_active)
8792       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8793     element = Tile[newx][newy] = Store[x][y];
8794
8795     InitField(newx, newy, FALSE);
8796   }
8797   else if (element == EL_BD_MAGIC_WALL_FILLING)
8798   {
8799     element = Tile[newx][newy] = get_next_element(element);
8800     if (!game.magic_wall_active)
8801       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8802     Store[newx][newy] = Store[x][y];
8803   }
8804   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8805   {
8806     Tile[x][y] = get_next_element(element);
8807     if (!game.magic_wall_active)
8808       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8809     element = Tile[newx][newy] = Store[x][y];
8810
8811     InitField(newx, newy, FALSE);
8812   }
8813   else if (element == EL_DC_MAGIC_WALL_FILLING)
8814   {
8815     element = Tile[newx][newy] = get_next_element(element);
8816     if (!game.magic_wall_active)
8817       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8818     Store[newx][newy] = Store[x][y];
8819   }
8820   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8821   {
8822     Tile[x][y] = get_next_element(element);
8823     if (!game.magic_wall_active)
8824       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8825     element = Tile[newx][newy] = Store[x][y];
8826
8827     InitField(newx, newy, FALSE);
8828   }
8829   else if (element == EL_AMOEBA_DROPPING)
8830   {
8831     Tile[x][y] = get_next_element(element);
8832     element = Tile[newx][newy] = Store[x][y];
8833   }
8834   else if (element == EL_SOKOBAN_OBJECT)
8835   {
8836     if (Back[x][y])
8837       Tile[x][y] = Back[x][y];
8838
8839     if (Back[newx][newy])
8840       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8841
8842     Back[x][y] = Back[newx][newy] = 0;
8843   }
8844
8845   Store[x][y] = EL_EMPTY;
8846   MovPos[x][y] = 0;
8847   MovDir[x][y] = 0;
8848   MovDelay[x][y] = 0;
8849
8850   MovDelay[newx][newy] = 0;
8851
8852   if (CAN_CHANGE_OR_HAS_ACTION(element))
8853   {
8854     // copy element change control values to new field
8855     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8856     ChangePage[newx][newy]  = ChangePage[x][y];
8857     ChangeCount[newx][newy] = ChangeCount[x][y];
8858     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8859   }
8860
8861   CustomValue[newx][newy] = CustomValue[x][y];
8862
8863   ChangeDelay[x][y] = 0;
8864   ChangePage[x][y] = -1;
8865   ChangeCount[x][y] = 0;
8866   ChangeEvent[x][y] = -1;
8867
8868   CustomValue[x][y] = 0;
8869
8870   // copy animation control values to new field
8871   GfxFrame[newx][newy]  = GfxFrame[x][y];
8872   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8873   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8874   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8875
8876   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8877
8878   // some elements can leave other elements behind after moving
8879   if (ei->move_leave_element != EL_EMPTY &&
8880       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8881       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8882   {
8883     int move_leave_element = ei->move_leave_element;
8884
8885     // this makes it possible to leave the removed element again
8886     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8887       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8888
8889     Tile[x][y] = move_leave_element;
8890
8891     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8892       MovDir[x][y] = direction;
8893
8894     InitField(x, y, FALSE);
8895
8896     if (GFX_CRUMBLED(Tile[x][y]))
8897       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8898
8899     if (IS_PLAYER_ELEMENT(move_leave_element))
8900       RelocatePlayer(x, y, move_leave_element);
8901   }
8902
8903   // do this after checking for left-behind element
8904   ResetGfxAnimation(x, y);      // reset animation values for old field
8905
8906   if (!CAN_MOVE(element) ||
8907       (CAN_FALL(element) && direction == MV_DOWN &&
8908        (element == EL_SPRING ||
8909         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8910         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8911     GfxDir[x][y] = MovDir[newx][newy] = 0;
8912
8913   TEST_DrawLevelField(x, y);
8914   TEST_DrawLevelField(newx, newy);
8915
8916   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8917
8918   // prevent pushed element from moving on in pushed direction
8919   if (pushed_by_player && CAN_MOVE(element) &&
8920       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8921       !(element_info[element].move_pattern & direction))
8922     TurnRound(newx, newy);
8923
8924   // prevent elements on conveyor belt from moving on in last direction
8925   if (pushed_by_conveyor && CAN_FALL(element) &&
8926       direction & MV_HORIZONTAL)
8927     MovDir[newx][newy] = 0;
8928
8929   if (!pushed_by_player)
8930   {
8931     int nextx = newx + dx, nexty = newy + dy;
8932     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8933
8934     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8935
8936     if (CAN_FALL(element) && direction == MV_DOWN)
8937       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8938
8939     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8940       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8941
8942     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8943       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8944   }
8945
8946   if (DONT_TOUCH(element))      // object may be nasty to player or others
8947   {
8948     TestIfBadThingTouchesPlayer(newx, newy);
8949     TestIfBadThingTouchesFriend(newx, newy);
8950
8951     if (!IS_CUSTOM_ELEMENT(element))
8952       TestIfBadThingTouchesOtherBadThing(newx, newy);
8953   }
8954   else if (element == EL_PENGUIN)
8955     TestIfFriendTouchesBadThing(newx, newy);
8956
8957   if (DONT_GET_HIT_BY(element))
8958   {
8959     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8960   }
8961
8962   // give the player one last chance (one more frame) to move away
8963   if (CAN_FALL(element) && direction == MV_DOWN &&
8964       (last_line || (!IS_FREE(x, newy + 1) &&
8965                      (!IS_PLAYER(x, newy + 1) ||
8966                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8967     Impact(x, newy);
8968
8969   if (pushed_by_player && !game.use_change_when_pushing_bug)
8970   {
8971     int push_side = MV_DIR_OPPOSITE(direction);
8972     struct PlayerInfo *player = PLAYERINFO(x, y);
8973
8974     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8975                                player->index_bit, push_side);
8976     CheckTriggeredElementChangeByPlayer(newx, newy, element, CE_PLAYER_PUSHES_X,
8977                                         player->index_bit, push_side);
8978   }
8979
8980   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8981     MovDelay[newx][newy] = 1;
8982
8983   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8984
8985   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8986   TestIfElementHitsCustomElement(newx, newy, direction);
8987   TestIfPlayerTouchesCustomElement(newx, newy);
8988   TestIfElementTouchesCustomElement(newx, newy);
8989
8990   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8991       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8992     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8993                              MV_DIR_OPPOSITE(direction));
8994 }
8995
8996 int AmoebaNeighbourNr(int ax, int ay)
8997 {
8998   int i;
8999   int element = Tile[ax][ay];
9000   int group_nr = 0;
9001   struct XY *xy = xy_topdown;
9002
9003   for (i = 0; i < NUM_DIRECTIONS; i++)
9004   {
9005     int x = ax + xy[i].x;
9006     int y = ay + xy[i].y;
9007
9008     if (!IN_LEV_FIELD(x, y))
9009       continue;
9010
9011     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
9012       group_nr = AmoebaNr[x][y];
9013   }
9014
9015   return group_nr;
9016 }
9017
9018 static void AmoebaMerge(int ax, int ay)
9019 {
9020   int i, x, y, xx, yy;
9021   int new_group_nr = AmoebaNr[ax][ay];
9022   struct XY *xy = xy_topdown;
9023
9024   if (new_group_nr == 0)
9025     return;
9026
9027   for (i = 0; i < NUM_DIRECTIONS; i++)
9028   {
9029     x = ax + xy[i].x;
9030     y = ay + xy[i].y;
9031
9032     if (!IN_LEV_FIELD(x, y))
9033       continue;
9034
9035     if ((Tile[x][y] == EL_AMOEBA_FULL ||
9036          Tile[x][y] == EL_BD_AMOEBA ||
9037          Tile[x][y] == EL_AMOEBA_DEAD) &&
9038         AmoebaNr[x][y] != new_group_nr)
9039     {
9040       int old_group_nr = AmoebaNr[x][y];
9041
9042       if (old_group_nr == 0)
9043         return;
9044
9045       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9046       AmoebaCnt[old_group_nr] = 0;
9047       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9048       AmoebaCnt2[old_group_nr] = 0;
9049
9050       SCAN_PLAYFIELD(xx, yy)
9051       {
9052         if (AmoebaNr[xx][yy] == old_group_nr)
9053           AmoebaNr[xx][yy] = new_group_nr;
9054       }
9055     }
9056   }
9057 }
9058
9059 void AmoebaToDiamond(int ax, int ay)
9060 {
9061   int i, x, y;
9062
9063   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
9064   {
9065     int group_nr = AmoebaNr[ax][ay];
9066
9067 #ifdef DEBUG
9068     if (group_nr == 0)
9069     {
9070       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
9071       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
9072
9073       return;
9074     }
9075 #endif
9076
9077     SCAN_PLAYFIELD(x, y)
9078     {
9079       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9080       {
9081         AmoebaNr[x][y] = 0;
9082         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
9083       }
9084     }
9085
9086     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9087                             SND_AMOEBA_TURNING_TO_GEM :
9088                             SND_AMOEBA_TURNING_TO_ROCK));
9089     Bang(ax, ay);
9090   }
9091   else
9092   {
9093     struct XY *xy = xy_topdown;
9094
9095     for (i = 0; i < NUM_DIRECTIONS; i++)
9096     {
9097       x = ax + xy[i].x;
9098       y = ay + xy[i].y;
9099
9100       if (!IN_LEV_FIELD(x, y))
9101         continue;
9102
9103       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9104       {
9105         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9106                               SND_AMOEBA_TURNING_TO_GEM :
9107                               SND_AMOEBA_TURNING_TO_ROCK));
9108         Bang(x, y);
9109       }
9110     }
9111   }
9112 }
9113
9114 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9115 {
9116   int x, y;
9117   int group_nr = AmoebaNr[ax][ay];
9118   boolean done = FALSE;
9119
9120 #ifdef DEBUG
9121   if (group_nr == 0)
9122   {
9123     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9124     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9125
9126     return;
9127   }
9128 #endif
9129
9130   SCAN_PLAYFIELD(x, y)
9131   {
9132     if (AmoebaNr[x][y] == group_nr &&
9133         (Tile[x][y] == EL_AMOEBA_DEAD ||
9134          Tile[x][y] == EL_BD_AMOEBA ||
9135          Tile[x][y] == EL_AMOEBA_GROWING))
9136     {
9137       AmoebaNr[x][y] = 0;
9138       Tile[x][y] = new_element;
9139       InitField(x, y, FALSE);
9140       TEST_DrawLevelField(x, y);
9141       done = TRUE;
9142     }
9143   }
9144
9145   if (done)
9146     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9147                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9148                             SND_BD_AMOEBA_TURNING_TO_GEM));
9149 }
9150
9151 static void AmoebaGrowing(int x, int y)
9152 {
9153   static DelayCounter sound_delay = { 0 };
9154
9155   if (!MovDelay[x][y])          // start new growing cycle
9156   {
9157     MovDelay[x][y] = 7;
9158
9159     if (DelayReached(&sound_delay))
9160     {
9161       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9162       sound_delay.value = 30;
9163     }
9164   }
9165
9166   if (MovDelay[x][y])           // wait some time before growing bigger
9167   {
9168     MovDelay[x][y]--;
9169     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9170     {
9171       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9172                                            6 - MovDelay[x][y]);
9173
9174       DrawLevelGraphic(x, y, IMG_AMOEBA_GROWING, frame);
9175     }
9176
9177     if (!MovDelay[x][y])
9178     {
9179       Tile[x][y] = Store[x][y];
9180       Store[x][y] = 0;
9181       TEST_DrawLevelField(x, y);
9182     }
9183   }
9184 }
9185
9186 static void AmoebaShrinking(int x, int y)
9187 {
9188   static DelayCounter sound_delay = { 0 };
9189
9190   if (!MovDelay[x][y])          // start new shrinking cycle
9191   {
9192     MovDelay[x][y] = 7;
9193
9194     if (DelayReached(&sound_delay))
9195       sound_delay.value = 30;
9196   }
9197
9198   if (MovDelay[x][y])           // wait some time before shrinking
9199   {
9200     MovDelay[x][y]--;
9201     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9202     {
9203       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9204                                            6 - MovDelay[x][y]);
9205
9206       DrawLevelGraphic(x, y, IMG_AMOEBA_SHRINKING, frame);
9207     }
9208
9209     if (!MovDelay[x][y])
9210     {
9211       Tile[x][y] = EL_EMPTY;
9212       TEST_DrawLevelField(x, y);
9213
9214       // don't let mole enter this field in this cycle;
9215       // (give priority to objects falling to this field from above)
9216       Stop[x][y] = TRUE;
9217     }
9218   }
9219 }
9220
9221 static void AmoebaReproduce(int ax, int ay)
9222 {
9223   int i;
9224   int element = Tile[ax][ay];
9225   int graphic = el2img(element);
9226   int newax = ax, neway = ay;
9227   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9228   struct XY *xy = xy_topdown;
9229
9230   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9231   {
9232     Tile[ax][ay] = EL_AMOEBA_DEAD;
9233     TEST_DrawLevelField(ax, ay);
9234     return;
9235   }
9236
9237   if (IS_ANIMATED(graphic))
9238     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9239
9240   if (!MovDelay[ax][ay])        // start making new amoeba field
9241     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9242
9243   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9244   {
9245     MovDelay[ax][ay]--;
9246     if (MovDelay[ax][ay])
9247       return;
9248   }
9249
9250   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9251   {
9252     int start = RND(4);
9253     int x = ax + xy[start].x;
9254     int y = ay + xy[start].y;
9255
9256     if (!IN_LEV_FIELD(x, y))
9257       return;
9258
9259     if (IS_FREE(x, y) ||
9260         CAN_GROW_INTO(Tile[x][y]) ||
9261         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9262         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9263     {
9264       newax = x;
9265       neway = y;
9266     }
9267
9268     if (newax == ax && neway == ay)
9269       return;
9270   }
9271   else                          // normal or "filled" (BD style) amoeba
9272   {
9273     int start = RND(4);
9274     boolean waiting_for_player = FALSE;
9275
9276     for (i = 0; i < NUM_DIRECTIONS; i++)
9277     {
9278       int j = (start + i) % 4;
9279       int x = ax + xy[j].x;
9280       int y = ay + xy[j].y;
9281
9282       if (!IN_LEV_FIELD(x, y))
9283         continue;
9284
9285       if (IS_FREE(x, y) ||
9286           CAN_GROW_INTO(Tile[x][y]) ||
9287           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9288           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9289       {
9290         newax = x;
9291         neway = y;
9292         break;
9293       }
9294       else if (IS_PLAYER(x, y))
9295         waiting_for_player = TRUE;
9296     }
9297
9298     if (newax == ax && neway == ay)             // amoeba cannot grow
9299     {
9300       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9301       {
9302         Tile[ax][ay] = EL_AMOEBA_DEAD;
9303         TEST_DrawLevelField(ax, ay);
9304         AmoebaCnt[AmoebaNr[ax][ay]]--;
9305
9306         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9307         {
9308           if (element == EL_AMOEBA_FULL)
9309             AmoebaToDiamond(ax, ay);
9310           else if (element == EL_BD_AMOEBA)
9311             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9312         }
9313       }
9314       return;
9315     }
9316     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9317     {
9318       // amoeba gets larger by growing in some direction
9319
9320       int new_group_nr = AmoebaNr[ax][ay];
9321
9322 #ifdef DEBUG
9323   if (new_group_nr == 0)
9324   {
9325     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9326           newax, neway);
9327     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9328
9329     return;
9330   }
9331 #endif
9332
9333       AmoebaNr[newax][neway] = new_group_nr;
9334       AmoebaCnt[new_group_nr]++;
9335       AmoebaCnt2[new_group_nr]++;
9336
9337       // if amoeba touches other amoeba(s) after growing, unify them
9338       AmoebaMerge(newax, neway);
9339
9340       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9341       {
9342         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9343         return;
9344       }
9345     }
9346   }
9347
9348   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9349       (neway == lev_fieldy - 1 && newax != ax))
9350   {
9351     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9352     Store[newax][neway] = element;
9353   }
9354   else if (neway == ay || element == EL_EMC_DRIPPER)
9355   {
9356     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9357
9358     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9359   }
9360   else
9361   {
9362     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9363     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9364     Store[ax][ay] = EL_AMOEBA_DROP;
9365     ContinueMoving(ax, ay);
9366     return;
9367   }
9368
9369   TEST_DrawLevelField(newax, neway);
9370 }
9371
9372 static void Life(int ax, int ay)
9373 {
9374   int x1, y1, x2, y2;
9375   int life_time = 40;
9376   int element = Tile[ax][ay];
9377   int graphic = el2img(element);
9378   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9379                          level.biomaze);
9380   boolean changed = FALSE;
9381
9382   if (IS_ANIMATED(graphic))
9383     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9384
9385   if (Stop[ax][ay])
9386     return;
9387
9388   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9389     MovDelay[ax][ay] = life_time;
9390
9391   if (MovDelay[ax][ay])         // wait some time before next cycle
9392   {
9393     MovDelay[ax][ay]--;
9394     if (MovDelay[ax][ay])
9395       return;
9396   }
9397
9398   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9399   {
9400     int xx = ax+x1, yy = ay+y1;
9401     int old_element = Tile[xx][yy];
9402     int num_neighbours = 0;
9403
9404     if (!IN_LEV_FIELD(xx, yy))
9405       continue;
9406
9407     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9408     {
9409       int x = xx+x2, y = yy+y2;
9410
9411       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9412         continue;
9413
9414       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9415       boolean is_neighbour = FALSE;
9416
9417       if (level.use_life_bugs)
9418         is_neighbour =
9419           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9420            (IS_FREE(x, y)                             &&  Stop[x][y]));
9421       else
9422         is_neighbour =
9423           (Last[x][y] == element || is_player_cell);
9424
9425       if (is_neighbour)
9426         num_neighbours++;
9427     }
9428
9429     boolean is_free = FALSE;
9430
9431     if (level.use_life_bugs)
9432       is_free = (IS_FREE(xx, yy));
9433     else
9434       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9435
9436     if (xx == ax && yy == ay)           // field in the middle
9437     {
9438       if (num_neighbours < life_parameter[0] ||
9439           num_neighbours > life_parameter[1])
9440       {
9441         Tile[xx][yy] = EL_EMPTY;
9442         if (Tile[xx][yy] != old_element)
9443           TEST_DrawLevelField(xx, yy);
9444         Stop[xx][yy] = TRUE;
9445         changed = TRUE;
9446       }
9447     }
9448     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9449     {                                   // free border field
9450       if (num_neighbours >= life_parameter[2] &&
9451           num_neighbours <= life_parameter[3])
9452       {
9453         Tile[xx][yy] = element;
9454         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time - 1);
9455         if (Tile[xx][yy] != old_element)
9456           TEST_DrawLevelField(xx, yy);
9457         Stop[xx][yy] = TRUE;
9458         changed = TRUE;
9459       }
9460     }
9461   }
9462
9463   if (changed)
9464     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9465                    SND_GAME_OF_LIFE_GROWING);
9466 }
9467
9468 static void InitRobotWheel(int x, int y)
9469 {
9470   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9471 }
9472
9473 static void RunRobotWheel(int x, int y)
9474 {
9475   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9476 }
9477
9478 static void StopRobotWheel(int x, int y)
9479 {
9480   if (game.robot_wheel_x == x &&
9481       game.robot_wheel_y == y)
9482   {
9483     game.robot_wheel_x = -1;
9484     game.robot_wheel_y = -1;
9485     game.robot_wheel_active = FALSE;
9486   }
9487 }
9488
9489 static void InitTimegateWheel(int x, int y)
9490 {
9491   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9492 }
9493
9494 static void RunTimegateWheel(int x, int y)
9495 {
9496   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9497 }
9498
9499 static void InitMagicBallDelay(int x, int y)
9500 {
9501   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9502 }
9503
9504 static void ActivateMagicBall(int bx, int by)
9505 {
9506   int x, y;
9507
9508   if (level.ball_random)
9509   {
9510     int pos_border = RND(8);    // select one of the eight border elements
9511     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9512     int xx = pos_content % 3;
9513     int yy = pos_content / 3;
9514
9515     x = bx - 1 + xx;
9516     y = by - 1 + yy;
9517
9518     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9519       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9520   }
9521   else
9522   {
9523     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9524     {
9525       int xx = x - bx + 1;
9526       int yy = y - by + 1;
9527
9528       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9529         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9530     }
9531   }
9532
9533   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9534 }
9535
9536 static void CheckExit(int x, int y)
9537 {
9538   if (game.gems_still_needed > 0 ||
9539       game.sokoban_fields_still_needed > 0 ||
9540       game.sokoban_objects_still_needed > 0 ||
9541       game.lights_still_needed > 0)
9542   {
9543     int element = Tile[x][y];
9544     int graphic = el2img(element);
9545
9546     if (IS_ANIMATED(graphic))
9547       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9548
9549     return;
9550   }
9551
9552   // do not re-open exit door closed after last player
9553   if (game.all_players_gone)
9554     return;
9555
9556   Tile[x][y] = EL_EXIT_OPENING;
9557
9558   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9559 }
9560
9561 static void CheckExitEM(int x, int y)
9562 {
9563   if (game.gems_still_needed > 0 ||
9564       game.sokoban_fields_still_needed > 0 ||
9565       game.sokoban_objects_still_needed > 0 ||
9566       game.lights_still_needed > 0)
9567   {
9568     int element = Tile[x][y];
9569     int graphic = el2img(element);
9570
9571     if (IS_ANIMATED(graphic))
9572       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9573
9574     return;
9575   }
9576
9577   // do not re-open exit door closed after last player
9578   if (game.all_players_gone)
9579     return;
9580
9581   Tile[x][y] = EL_EM_EXIT_OPENING;
9582
9583   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9584 }
9585
9586 static void CheckExitSteel(int x, int y)
9587 {
9588   if (game.gems_still_needed > 0 ||
9589       game.sokoban_fields_still_needed > 0 ||
9590       game.sokoban_objects_still_needed > 0 ||
9591       game.lights_still_needed > 0)
9592   {
9593     int element = Tile[x][y];
9594     int graphic = el2img(element);
9595
9596     if (IS_ANIMATED(graphic))
9597       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9598
9599     return;
9600   }
9601
9602   // do not re-open exit door closed after last player
9603   if (game.all_players_gone)
9604     return;
9605
9606   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9607
9608   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9609 }
9610
9611 static void CheckExitSteelEM(int x, int y)
9612 {
9613   if (game.gems_still_needed > 0 ||
9614       game.sokoban_fields_still_needed > 0 ||
9615       game.sokoban_objects_still_needed > 0 ||
9616       game.lights_still_needed > 0)
9617   {
9618     int element = Tile[x][y];
9619     int graphic = el2img(element);
9620
9621     if (IS_ANIMATED(graphic))
9622       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9623
9624     return;
9625   }
9626
9627   // do not re-open exit door closed after last player
9628   if (game.all_players_gone)
9629     return;
9630
9631   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9632
9633   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9634 }
9635
9636 static void CheckExitSP(int x, int y)
9637 {
9638   if (game.gems_still_needed > 0)
9639   {
9640     int element = Tile[x][y];
9641     int graphic = el2img(element);
9642
9643     if (IS_ANIMATED(graphic))
9644       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9645
9646     return;
9647   }
9648
9649   // do not re-open exit door closed after last player
9650   if (game.all_players_gone)
9651     return;
9652
9653   Tile[x][y] = EL_SP_EXIT_OPENING;
9654
9655   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9656 }
9657
9658 static void CloseAllOpenTimegates(void)
9659 {
9660   int x, y;
9661
9662   SCAN_PLAYFIELD(x, y)
9663   {
9664     int element = Tile[x][y];
9665
9666     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9667     {
9668       Tile[x][y] = EL_TIMEGATE_CLOSING;
9669
9670       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9671     }
9672   }
9673 }
9674
9675 static void DrawTwinkleOnField(int x, int y)
9676 {
9677   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9678     return;
9679
9680   if (Tile[x][y] == EL_BD_DIAMOND)
9681     return;
9682
9683   if (MovDelay[x][y] == 0)      // next animation frame
9684     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9685
9686   if (MovDelay[x][y] != 0)      // wait some time before next frame
9687   {
9688     MovDelay[x][y]--;
9689
9690     DrawLevelElementAnimation(x, y, Tile[x][y]);
9691
9692     if (MovDelay[x][y] != 0)
9693     {
9694       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9695                                            10 - MovDelay[x][y]);
9696
9697       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9698     }
9699   }
9700 }
9701
9702 static void WallGrowing(int x, int y)
9703 {
9704   int delay = 6;
9705
9706   if (!MovDelay[x][y])          // next animation frame
9707     MovDelay[x][y] = 3 * delay;
9708
9709   if (MovDelay[x][y])           // wait some time before next frame
9710   {
9711     MovDelay[x][y]--;
9712
9713     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9714     {
9715       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9716       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9717
9718       DrawLevelGraphic(x, y, graphic, frame);
9719     }
9720
9721     if (!MovDelay[x][y])
9722     {
9723       if (MovDir[x][y] == MV_LEFT)
9724       {
9725         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9726           TEST_DrawLevelField(x - 1, y);
9727       }
9728       else if (MovDir[x][y] == MV_RIGHT)
9729       {
9730         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9731           TEST_DrawLevelField(x + 1, y);
9732       }
9733       else if (MovDir[x][y] == MV_UP)
9734       {
9735         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9736           TEST_DrawLevelField(x, y - 1);
9737       }
9738       else
9739       {
9740         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9741           TEST_DrawLevelField(x, y + 1);
9742       }
9743
9744       Tile[x][y] = Store[x][y];
9745       Store[x][y] = 0;
9746       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9747       TEST_DrawLevelField(x, y);
9748     }
9749   }
9750 }
9751
9752 static void CheckWallGrowing(int ax, int ay)
9753 {
9754   int element = Tile[ax][ay];
9755   int graphic = el2img(element);
9756   boolean free_top    = FALSE;
9757   boolean free_bottom = FALSE;
9758   boolean free_left   = FALSE;
9759   boolean free_right  = FALSE;
9760   boolean stop_top    = FALSE;
9761   boolean stop_bottom = FALSE;
9762   boolean stop_left   = FALSE;
9763   boolean stop_right  = FALSE;
9764   boolean new_wall    = FALSE;
9765
9766   boolean is_steelwall  = (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9767                            element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9768                            element == EL_EXPANDABLE_STEELWALL_ANY);
9769
9770   boolean grow_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9771                              element == EL_EXPANDABLE_WALL_ANY ||
9772                              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9773                              element == EL_EXPANDABLE_STEELWALL_ANY);
9774
9775   boolean grow_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9776                              element == EL_EXPANDABLE_WALL_ANY ||
9777                              element == EL_EXPANDABLE_WALL ||
9778                              element == EL_BD_EXPANDABLE_WALL ||
9779                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9780                              element == EL_EXPANDABLE_STEELWALL_ANY);
9781
9782   boolean stop_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9783                              element == EL_EXPANDABLE_STEELWALL_VERTICAL);
9784
9785   boolean stop_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9786                              element == EL_EXPANDABLE_WALL ||
9787                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL);
9788
9789   int wall_growing = (is_steelwall ?
9790                       EL_EXPANDABLE_STEELWALL_GROWING :
9791                       EL_EXPANDABLE_WALL_GROWING);
9792
9793   int gfx_wall_growing_up    = (is_steelwall ?
9794                                 IMG_EXPANDABLE_STEELWALL_GROWING_UP :
9795                                 IMG_EXPANDABLE_WALL_GROWING_UP);
9796   int gfx_wall_growing_down  = (is_steelwall ?
9797                                 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN :
9798                                 IMG_EXPANDABLE_WALL_GROWING_DOWN);
9799   int gfx_wall_growing_left  = (is_steelwall ?
9800                                 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT :
9801                                 IMG_EXPANDABLE_WALL_GROWING_LEFT);
9802   int gfx_wall_growing_right = (is_steelwall ?
9803                                 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT :
9804                                 IMG_EXPANDABLE_WALL_GROWING_RIGHT);
9805
9806   if (IS_ANIMATED(graphic))
9807     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9808
9809   if (!MovDelay[ax][ay])        // start building new wall
9810     MovDelay[ax][ay] = 6;
9811
9812   if (MovDelay[ax][ay])         // wait some time before building new wall
9813   {
9814     MovDelay[ax][ay]--;
9815     if (MovDelay[ax][ay])
9816       return;
9817   }
9818
9819   if (IN_LEV_FIELD(ax, ay - 1) && IS_FREE(ax, ay - 1))
9820     free_top = TRUE;
9821   if (IN_LEV_FIELD(ax, ay + 1) && IS_FREE(ax, ay + 1))
9822     free_bottom = TRUE;
9823   if (IN_LEV_FIELD(ax - 1, ay) && IS_FREE(ax - 1, ay))
9824     free_left = TRUE;
9825   if (IN_LEV_FIELD(ax + 1, ay) && IS_FREE(ax + 1, ay))
9826     free_right = TRUE;
9827
9828   if (grow_vertical)
9829   {
9830     if (free_top)
9831     {
9832       Tile[ax][ay - 1] = wall_growing;
9833       Store[ax][ay - 1] = element;
9834       GfxDir[ax][ay - 1] = MovDir[ax][ay - 1] = MV_UP;
9835
9836       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay - 1)))
9837         DrawLevelGraphic(ax, ay - 1, gfx_wall_growing_up, 0);
9838
9839       new_wall = TRUE;
9840     }
9841
9842     if (free_bottom)
9843     {
9844       Tile[ax][ay + 1] = wall_growing;
9845       Store[ax][ay + 1] = element;
9846       GfxDir[ax][ay + 1] = MovDir[ax][ay + 1] = MV_DOWN;
9847
9848       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay + 1)))
9849         DrawLevelGraphic(ax, ay + 1, gfx_wall_growing_down, 0);
9850
9851       new_wall = TRUE;
9852     }
9853   }
9854
9855   if (grow_horizontal)
9856   {
9857     if (free_left)
9858     {
9859       Tile[ax - 1][ay] = wall_growing;
9860       Store[ax - 1][ay] = element;
9861       GfxDir[ax - 1][ay] = MovDir[ax - 1][ay] = MV_LEFT;
9862
9863       if (IN_SCR_FIELD(SCREENX(ax - 1), SCREENY(ay)))
9864         DrawLevelGraphic(ax - 1, ay, gfx_wall_growing_left, 0);
9865
9866       new_wall = TRUE;
9867     }
9868
9869     if (free_right)
9870     {
9871       Tile[ax + 1][ay] = wall_growing;
9872       Store[ax + 1][ay] = element;
9873       GfxDir[ax + 1][ay] = MovDir[ax + 1][ay] = MV_RIGHT;
9874
9875       if (IN_SCR_FIELD(SCREENX(ax + 1), SCREENY(ay)))
9876         DrawLevelGraphic(ax + 1, ay, gfx_wall_growing_right, 0);
9877
9878       new_wall = TRUE;
9879     }
9880   }
9881
9882   if (element == EL_EXPANDABLE_WALL && (free_left || free_right))
9883     TEST_DrawLevelField(ax, ay);
9884
9885   if (!IN_LEV_FIELD(ax, ay - 1) || IS_WALL(Tile[ax][ay - 1]))
9886     stop_top = TRUE;
9887   if (!IN_LEV_FIELD(ax, ay + 1) || IS_WALL(Tile[ax][ay + 1]))
9888     stop_bottom = TRUE;
9889   if (!IN_LEV_FIELD(ax - 1, ay) || IS_WALL(Tile[ax - 1][ay]))
9890     stop_left = TRUE;
9891   if (!IN_LEV_FIELD(ax + 1, ay) || IS_WALL(Tile[ax + 1][ay]))
9892     stop_right = TRUE;
9893
9894   if (((stop_top && stop_bottom) || stop_horizontal) &&
9895       ((stop_left && stop_right) || stop_vertical))
9896     Tile[ax][ay] = (is_steelwall ? EL_STEELWALL : EL_WALL);
9897
9898   if (new_wall)
9899     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9900 }
9901
9902 static void CheckForDragon(int x, int y)
9903 {
9904   int i, j;
9905   boolean dragon_found = FALSE;
9906   struct XY *xy = xy_topdown;
9907
9908   for (i = 0; i < NUM_DIRECTIONS; i++)
9909   {
9910     for (j = 0; j < 4; j++)
9911     {
9912       int xx = x + j * xy[i].x;
9913       int yy = y + j * xy[i].y;
9914
9915       if (IN_LEV_FIELD(xx, yy) &&
9916           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9917       {
9918         if (Tile[xx][yy] == EL_DRAGON)
9919           dragon_found = TRUE;
9920       }
9921       else
9922         break;
9923     }
9924   }
9925
9926   if (!dragon_found)
9927   {
9928     for (i = 0; i < NUM_DIRECTIONS; i++)
9929     {
9930       for (j = 0; j < 3; j++)
9931       {
9932         int xx = x + j * xy[i].x;
9933         int yy = y + j * xy[i].y;
9934
9935         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9936         {
9937           Tile[xx][yy] = EL_EMPTY;
9938           TEST_DrawLevelField(xx, yy);
9939         }
9940         else
9941           break;
9942       }
9943     }
9944   }
9945 }
9946
9947 static void InitBuggyBase(int x, int y)
9948 {
9949   int element = Tile[x][y];
9950   int activating_delay = FRAMES_PER_SECOND / 4;
9951
9952   ChangeDelay[x][y] =
9953     (element == EL_SP_BUGGY_BASE ?
9954      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9955      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9956      activating_delay :
9957      element == EL_SP_BUGGY_BASE_ACTIVE ?
9958      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9959 }
9960
9961 static void WarnBuggyBase(int x, int y)
9962 {
9963   int i;
9964   struct XY *xy = xy_topdown;
9965
9966   for (i = 0; i < NUM_DIRECTIONS; i++)
9967   {
9968     int xx = x + xy[i].x;
9969     int yy = y + xy[i].y;
9970
9971     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9972     {
9973       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9974
9975       break;
9976     }
9977   }
9978 }
9979
9980 static void InitTrap(int x, int y)
9981 {
9982   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9983 }
9984
9985 static void ActivateTrap(int x, int y)
9986 {
9987   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9988 }
9989
9990 static void ChangeActiveTrap(int x, int y)
9991 {
9992   int graphic = IMG_TRAP_ACTIVE;
9993
9994   // if new animation frame was drawn, correct crumbled sand border
9995   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9996     TEST_DrawLevelFieldCrumbled(x, y);
9997 }
9998
9999 static int getSpecialActionElement(int element, int number, int base_element)
10000 {
10001   return (element != EL_EMPTY ? element :
10002           number != -1 ? base_element + number - 1 :
10003           EL_EMPTY);
10004 }
10005
10006 static int getModifiedActionNumber(int value_old, int operator, int operand,
10007                                    int value_min, int value_max)
10008 {
10009   int value_new = (operator == CA_MODE_SET      ? operand :
10010                    operator == CA_MODE_ADD      ? value_old + operand :
10011                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10012                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10013                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10014                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10015                    value_old);
10016
10017   return (value_new < value_min ? value_min :
10018           value_new > value_max ? value_max :
10019           value_new);
10020 }
10021
10022 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10023 {
10024   struct ElementInfo *ei = &element_info[element];
10025   struct ElementChangeInfo *change = &ei->change_page[page];
10026   int target_element = change->target_element;
10027   int action_type = change->action_type;
10028   int action_mode = change->action_mode;
10029   int action_arg = change->action_arg;
10030   int action_element = change->action_element;
10031   int i;
10032
10033   if (!change->has_action)
10034     return;
10035
10036   // ---------- determine action paramater values -----------------------------
10037
10038   int level_time_value =
10039     (level.time > 0 ? TimeLeft :
10040      TimePlayed);
10041
10042   int action_arg_element_raw =
10043     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10044      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10045      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10046      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10047      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10048      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10049      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10050      EL_EMPTY);
10051   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10052
10053   int action_arg_direction =
10054     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10055      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10056      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10057      change->actual_trigger_side :
10058      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10059      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10060      MV_NONE);
10061
10062   int action_arg_number_min =
10063     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10064      CA_ARG_MIN);
10065
10066   int action_arg_number_max =
10067     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10068      action_type == CA_SET_LEVEL_GEMS ? 999 :
10069      action_type == CA_SET_LEVEL_TIME ? 9999 :
10070      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10071      action_type == CA_SET_CE_VALUE ? 9999 :
10072      action_type == CA_SET_CE_SCORE ? 9999 :
10073      CA_ARG_MAX);
10074
10075   int action_arg_number_reset =
10076     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10077      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10078      action_type == CA_SET_LEVEL_TIME ? level.time :
10079      action_type == CA_SET_LEVEL_SCORE ? 0 :
10080      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10081      action_type == CA_SET_CE_SCORE ? 0 :
10082      0);
10083
10084   int action_arg_number =
10085     (action_arg <= CA_ARG_MAX ? action_arg :
10086      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10087      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10088      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10089      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10090      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10091      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10092      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10093      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10094      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10095      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10096      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10097      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10098      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10099      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10100      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10101      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10102      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10103      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10104      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10105      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10106      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10107      -1);
10108
10109   int action_arg_number_old =
10110     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10111      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10112      action_type == CA_SET_LEVEL_SCORE ? game.score :
10113      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10114      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10115      0);
10116
10117   int action_arg_number_new =
10118     getModifiedActionNumber(action_arg_number_old,
10119                             action_mode, action_arg_number,
10120                             action_arg_number_min, action_arg_number_max);
10121
10122   int trigger_player_bits =
10123     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10124      change->actual_trigger_player_bits : change->trigger_player);
10125
10126   int action_arg_player_bits =
10127     (action_arg >= CA_ARG_PLAYER_1 &&
10128      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10129      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10130      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10131      PLAYER_BITS_ANY);
10132
10133   // ---------- execute action  -----------------------------------------------
10134
10135   switch (action_type)
10136   {
10137     case CA_NO_ACTION:
10138     {
10139       return;
10140     }
10141
10142     // ---------- level actions  ----------------------------------------------
10143
10144     case CA_RESTART_LEVEL:
10145     {
10146       game.restart_level = TRUE;
10147
10148       break;
10149     }
10150
10151     case CA_SHOW_ENVELOPE:
10152     {
10153       int element = getSpecialActionElement(action_arg_element,
10154                                             action_arg_number, EL_ENVELOPE_1);
10155
10156       if (IS_ENVELOPE(element))
10157         local_player->show_envelope = element;
10158
10159       break;
10160     }
10161
10162     case CA_SET_LEVEL_TIME:
10163     {
10164       if (level.time > 0)       // only modify limited time value
10165       {
10166         TimeLeft = action_arg_number_new;
10167
10168         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10169
10170         DisplayGameControlValues();
10171
10172         if (!TimeLeft && game.time_limit)
10173           for (i = 0; i < MAX_PLAYERS; i++)
10174             KillPlayer(&stored_player[i]);
10175       }
10176
10177       break;
10178     }
10179
10180     case CA_SET_LEVEL_SCORE:
10181     {
10182       game.score = action_arg_number_new;
10183
10184       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10185
10186       DisplayGameControlValues();
10187
10188       break;
10189     }
10190
10191     case CA_SET_LEVEL_GEMS:
10192     {
10193       game.gems_still_needed = action_arg_number_new;
10194
10195       game.snapshot.collected_item = TRUE;
10196
10197       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10198
10199       DisplayGameControlValues();
10200
10201       break;
10202     }
10203
10204     case CA_SET_LEVEL_WIND:
10205     {
10206       game.wind_direction = action_arg_direction;
10207
10208       break;
10209     }
10210
10211     case CA_SET_LEVEL_RANDOM_SEED:
10212     {
10213       // ensure that setting a new random seed while playing is predictable
10214       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10215
10216       break;
10217     }
10218
10219     // ---------- player actions  ---------------------------------------------
10220
10221     case CA_MOVE_PLAYER:
10222     case CA_MOVE_PLAYER_NEW:
10223     {
10224       // automatically move to the next field in specified direction
10225       for (i = 0; i < MAX_PLAYERS; i++)
10226         if (trigger_player_bits & (1 << i))
10227           if (action_type == CA_MOVE_PLAYER ||
10228               stored_player[i].MovPos == 0)
10229             stored_player[i].programmed_action = action_arg_direction;
10230
10231       break;
10232     }
10233
10234     case CA_EXIT_PLAYER:
10235     {
10236       for (i = 0; i < MAX_PLAYERS; i++)
10237         if (action_arg_player_bits & (1 << i))
10238           ExitPlayer(&stored_player[i]);
10239
10240       if (game.players_still_needed == 0)
10241         LevelSolved();
10242
10243       break;
10244     }
10245
10246     case CA_KILL_PLAYER:
10247     {
10248       for (i = 0; i < MAX_PLAYERS; i++)
10249         if (action_arg_player_bits & (1 << i))
10250           KillPlayer(&stored_player[i]);
10251
10252       break;
10253     }
10254
10255     case CA_SET_PLAYER_KEYS:
10256     {
10257       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10258       int element = getSpecialActionElement(action_arg_element,
10259                                             action_arg_number, EL_KEY_1);
10260
10261       if (IS_KEY(element))
10262       {
10263         for (i = 0; i < MAX_PLAYERS; i++)
10264         {
10265           if (trigger_player_bits & (1 << i))
10266           {
10267             stored_player[i].key[KEY_NR(element)] = key_state;
10268
10269             DrawGameDoorValues();
10270           }
10271         }
10272       }
10273
10274       break;
10275     }
10276
10277     case CA_SET_PLAYER_SPEED:
10278     {
10279       for (i = 0; i < MAX_PLAYERS; i++)
10280       {
10281         if (trigger_player_bits & (1 << i))
10282         {
10283           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10284
10285           if (action_arg == CA_ARG_SPEED_FASTER &&
10286               stored_player[i].cannot_move)
10287           {
10288             action_arg_number = STEPSIZE_VERY_SLOW;
10289           }
10290           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10291                    action_arg == CA_ARG_SPEED_FASTER)
10292           {
10293             action_arg_number = 2;
10294             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10295                            CA_MODE_MULTIPLY);
10296           }
10297           else if (action_arg == CA_ARG_NUMBER_RESET)
10298           {
10299             action_arg_number = level.initial_player_stepsize[i];
10300           }
10301
10302           move_stepsize =
10303             getModifiedActionNumber(move_stepsize,
10304                                     action_mode,
10305                                     action_arg_number,
10306                                     action_arg_number_min,
10307                                     action_arg_number_max);
10308
10309           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10310         }
10311       }
10312
10313       break;
10314     }
10315
10316     case CA_SET_PLAYER_SHIELD:
10317     {
10318       for (i = 0; i < MAX_PLAYERS; i++)
10319       {
10320         if (trigger_player_bits & (1 << i))
10321         {
10322           if (action_arg == CA_ARG_SHIELD_OFF)
10323           {
10324             stored_player[i].shield_normal_time_left = 0;
10325             stored_player[i].shield_deadly_time_left = 0;
10326           }
10327           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10328           {
10329             stored_player[i].shield_normal_time_left = 999999;
10330           }
10331           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10332           {
10333             stored_player[i].shield_normal_time_left = 999999;
10334             stored_player[i].shield_deadly_time_left = 999999;
10335           }
10336         }
10337       }
10338
10339       break;
10340     }
10341
10342     case CA_SET_PLAYER_GRAVITY:
10343     {
10344       for (i = 0; i < MAX_PLAYERS; i++)
10345       {
10346         if (trigger_player_bits & (1 << i))
10347         {
10348           stored_player[i].gravity =
10349             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10350              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10351              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10352              stored_player[i].gravity);
10353         }
10354       }
10355
10356       break;
10357     }
10358
10359     case CA_SET_PLAYER_ARTWORK:
10360     {
10361       for (i = 0; i < MAX_PLAYERS; i++)
10362       {
10363         if (trigger_player_bits & (1 << i))
10364         {
10365           int artwork_element = action_arg_element;
10366
10367           if (action_arg == CA_ARG_ELEMENT_RESET)
10368             artwork_element =
10369               (level.use_artwork_element[i] ? level.artwork_element[i] :
10370                stored_player[i].element_nr);
10371
10372           if (stored_player[i].artwork_element != artwork_element)
10373             stored_player[i].Frame = 0;
10374
10375           stored_player[i].artwork_element = artwork_element;
10376
10377           SetPlayerWaiting(&stored_player[i], FALSE);
10378
10379           // set number of special actions for bored and sleeping animation
10380           stored_player[i].num_special_action_bored =
10381             get_num_special_action(artwork_element,
10382                                    ACTION_BORING_1, ACTION_BORING_LAST);
10383           stored_player[i].num_special_action_sleeping =
10384             get_num_special_action(artwork_element,
10385                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10386         }
10387       }
10388
10389       break;
10390     }
10391
10392     case CA_SET_PLAYER_INVENTORY:
10393     {
10394       for (i = 0; i < MAX_PLAYERS; i++)
10395       {
10396         struct PlayerInfo *player = &stored_player[i];
10397         int j, k;
10398
10399         if (trigger_player_bits & (1 << i))
10400         {
10401           int inventory_element = action_arg_element;
10402
10403           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10404               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10405               action_arg == CA_ARG_ELEMENT_ACTION)
10406           {
10407             int element = inventory_element;
10408             int collect_count = element_info[element].collect_count_initial;
10409
10410             if (!IS_CUSTOM_ELEMENT(element))
10411               collect_count = 1;
10412
10413             if (collect_count == 0)
10414               player->inventory_infinite_element = element;
10415             else
10416               for (k = 0; k < collect_count; k++)
10417                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10418                   player->inventory_element[player->inventory_size++] =
10419                     element;
10420           }
10421           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10422                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10423                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10424           {
10425             if (player->inventory_infinite_element != EL_UNDEFINED &&
10426                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10427                                      action_arg_element_raw))
10428               player->inventory_infinite_element = EL_UNDEFINED;
10429
10430             for (k = 0, j = 0; j < player->inventory_size; j++)
10431             {
10432               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10433                                         action_arg_element_raw))
10434                 player->inventory_element[k++] = player->inventory_element[j];
10435             }
10436
10437             player->inventory_size = k;
10438           }
10439           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10440           {
10441             if (player->inventory_size > 0)
10442             {
10443               for (j = 0; j < player->inventory_size - 1; j++)
10444                 player->inventory_element[j] = player->inventory_element[j + 1];
10445
10446               player->inventory_size--;
10447             }
10448           }
10449           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10450           {
10451             if (player->inventory_size > 0)
10452               player->inventory_size--;
10453           }
10454           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10455           {
10456             player->inventory_infinite_element = EL_UNDEFINED;
10457             player->inventory_size = 0;
10458           }
10459           else if (action_arg == CA_ARG_INVENTORY_RESET)
10460           {
10461             player->inventory_infinite_element = EL_UNDEFINED;
10462             player->inventory_size = 0;
10463
10464             if (level.use_initial_inventory[i])
10465             {
10466               for (j = 0; j < level.initial_inventory_size[i]; j++)
10467               {
10468                 int element = level.initial_inventory_content[i][j];
10469                 int collect_count = element_info[element].collect_count_initial;
10470
10471                 if (!IS_CUSTOM_ELEMENT(element))
10472                   collect_count = 1;
10473
10474                 if (collect_count == 0)
10475                   player->inventory_infinite_element = element;
10476                 else
10477                   for (k = 0; k < collect_count; k++)
10478                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10479                       player->inventory_element[player->inventory_size++] =
10480                         element;
10481               }
10482             }
10483           }
10484         }
10485       }
10486
10487       break;
10488     }
10489
10490     // ---------- CE actions  -------------------------------------------------
10491
10492     case CA_SET_CE_VALUE:
10493     {
10494       int last_ce_value = CustomValue[x][y];
10495
10496       CustomValue[x][y] = action_arg_number_new;
10497
10498       if (CustomValue[x][y] != last_ce_value)
10499       {
10500         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10501         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10502
10503         if (CustomValue[x][y] == 0)
10504         {
10505           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10506           ChangeCount[x][y] = 0;        // allow at least one more change
10507
10508           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10509           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10510         }
10511       }
10512
10513       break;
10514     }
10515
10516     case CA_SET_CE_SCORE:
10517     {
10518       int last_ce_score = ei->collect_score;
10519
10520       ei->collect_score = action_arg_number_new;
10521
10522       if (ei->collect_score != last_ce_score)
10523       {
10524         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10525         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10526
10527         if (ei->collect_score == 0)
10528         {
10529           int xx, yy;
10530
10531           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10532           ChangeCount[x][y] = 0;        // allow at least one more change
10533
10534           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10535           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10536
10537           /*
10538             This is a very special case that seems to be a mixture between
10539             CheckElementChange() and CheckTriggeredElementChange(): while
10540             the first one only affects single elements that are triggered
10541             directly, the second one affects multiple elements in the playfield
10542             that are triggered indirectly by another element. This is a third
10543             case: Changing the CE score always affects multiple identical CEs,
10544             so every affected CE must be checked, not only the single CE for
10545             which the CE score was changed in the first place (as every instance
10546             of that CE shares the same CE score, and therefore also can change)!
10547           */
10548           SCAN_PLAYFIELD(xx, yy)
10549           {
10550             if (Tile[xx][yy] == element)
10551               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10552                                  CE_SCORE_GETS_ZERO);
10553           }
10554         }
10555       }
10556
10557       break;
10558     }
10559
10560     case CA_SET_CE_ARTWORK:
10561     {
10562       int artwork_element = action_arg_element;
10563       boolean reset_frame = FALSE;
10564       int xx, yy;
10565
10566       if (action_arg == CA_ARG_ELEMENT_RESET)
10567         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10568                            element);
10569
10570       if (ei->gfx_element != artwork_element)
10571         reset_frame = TRUE;
10572
10573       ei->gfx_element = artwork_element;
10574
10575       SCAN_PLAYFIELD(xx, yy)
10576       {
10577         if (Tile[xx][yy] == element)
10578         {
10579           if (reset_frame)
10580           {
10581             ResetGfxAnimation(xx, yy);
10582             ResetRandomAnimationValue(xx, yy);
10583           }
10584
10585           TEST_DrawLevelField(xx, yy);
10586         }
10587       }
10588
10589       break;
10590     }
10591
10592     // ---------- engine actions  ---------------------------------------------
10593
10594     case CA_SET_ENGINE_SCAN_MODE:
10595     {
10596       InitPlayfieldScanMode(action_arg);
10597
10598       break;
10599     }
10600
10601     default:
10602       break;
10603   }
10604 }
10605
10606 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10607 {
10608   int old_element = Tile[x][y];
10609   int new_element = GetElementFromGroupElement(element);
10610   int previous_move_direction = MovDir[x][y];
10611   int last_ce_value = CustomValue[x][y];
10612   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10613   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10614   boolean add_player_onto_element = (new_element_is_player &&
10615                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10616                                      IS_WALKABLE(old_element));
10617
10618   if (!add_player_onto_element)
10619   {
10620     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10621       RemoveMovingField(x, y);
10622     else
10623       RemoveField(x, y);
10624
10625     Tile[x][y] = new_element;
10626
10627     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10628       MovDir[x][y] = previous_move_direction;
10629
10630     if (element_info[new_element].use_last_ce_value)
10631       CustomValue[x][y] = last_ce_value;
10632
10633     InitField_WithBug1(x, y, FALSE);
10634
10635     new_element = Tile[x][y];   // element may have changed
10636
10637     ResetGfxAnimation(x, y);
10638     ResetRandomAnimationValue(x, y);
10639
10640     TEST_DrawLevelField(x, y);
10641
10642     if (GFX_CRUMBLED(new_element))
10643       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10644
10645     // check if element under the player changes from accessible to unaccessible
10646     // (needed for special case of dropping element which then changes)
10647     // (must be checked after creating new element for walkable group elements)
10648     if (IS_PLAYER(x, y) && !player_explosion_protected &&
10649         IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10650     {
10651       KillPlayer(PLAYERINFO(x, y));
10652
10653       return;
10654     }
10655   }
10656
10657   // "ChangeCount" not set yet to allow "entered by player" change one time
10658   if (new_element_is_player)
10659     RelocatePlayer(x, y, new_element);
10660
10661   if (is_change)
10662     ChangeCount[x][y]++;        // count number of changes in the same frame
10663
10664   TestIfBadThingTouchesPlayer(x, y);
10665   TestIfPlayerTouchesCustomElement(x, y);
10666   TestIfElementTouchesCustomElement(x, y);
10667 }
10668
10669 static void CreateField(int x, int y, int element)
10670 {
10671   CreateFieldExt(x, y, element, FALSE);
10672 }
10673
10674 static void CreateElementFromChange(int x, int y, int element)
10675 {
10676   element = GET_VALID_RUNTIME_ELEMENT(element);
10677
10678   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10679   {
10680     int old_element = Tile[x][y];
10681
10682     // prevent changed element from moving in same engine frame
10683     // unless both old and new element can either fall or move
10684     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10685         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10686       Stop[x][y] = TRUE;
10687   }
10688
10689   CreateFieldExt(x, y, element, TRUE);
10690 }
10691
10692 static boolean ChangeElement(int x, int y, int element, int page)
10693 {
10694   struct ElementInfo *ei = &element_info[element];
10695   struct ElementChangeInfo *change = &ei->change_page[page];
10696   int ce_value = CustomValue[x][y];
10697   int ce_score = ei->collect_score;
10698   int target_element;
10699   int old_element = Tile[x][y];
10700
10701   // always use default change event to prevent running into a loop
10702   if (ChangeEvent[x][y] == -1)
10703     ChangeEvent[x][y] = CE_DELAY;
10704
10705   if (ChangeEvent[x][y] == CE_DELAY)
10706   {
10707     // reset actual trigger element, trigger player and action element
10708     change->actual_trigger_element = EL_EMPTY;
10709     change->actual_trigger_player = EL_EMPTY;
10710     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10711     change->actual_trigger_side = CH_SIDE_NONE;
10712     change->actual_trigger_ce_value = 0;
10713     change->actual_trigger_ce_score = 0;
10714   }
10715
10716   // do not change elements more than a specified maximum number of changes
10717   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10718     return FALSE;
10719
10720   ChangeCount[x][y]++;          // count number of changes in the same frame
10721
10722   if (ei->has_anim_event)
10723     HandleGlobalAnimEventByElementChange(element, page, x, y);
10724
10725   if (change->explode)
10726   {
10727     Bang(x, y);
10728
10729     return TRUE;
10730   }
10731
10732   if (change->use_target_content)
10733   {
10734     boolean complete_replace = TRUE;
10735     boolean can_replace[3][3];
10736     int xx, yy;
10737
10738     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10739     {
10740       boolean is_empty;
10741       boolean is_walkable;
10742       boolean is_diggable;
10743       boolean is_collectible;
10744       boolean is_removable;
10745       boolean is_destructible;
10746       int ex = x + xx - 1;
10747       int ey = y + yy - 1;
10748       int content_element = change->target_content.e[xx][yy];
10749       int e;
10750
10751       can_replace[xx][yy] = TRUE;
10752
10753       if (ex == x && ey == y)   // do not check changing element itself
10754         continue;
10755
10756       if (content_element == EL_EMPTY_SPACE)
10757       {
10758         can_replace[xx][yy] = FALSE;    // do not replace border with space
10759
10760         continue;
10761       }
10762
10763       if (!IN_LEV_FIELD(ex, ey))
10764       {
10765         can_replace[xx][yy] = FALSE;
10766         complete_replace = FALSE;
10767
10768         continue;
10769       }
10770
10771       e = Tile[ex][ey];
10772
10773       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10774         e = MovingOrBlocked2Element(ex, ey);
10775
10776       is_empty = (IS_FREE(ex, ey) ||
10777                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10778
10779       is_walkable     = (is_empty || IS_WALKABLE(e));
10780       is_diggable     = (is_empty || IS_DIGGABLE(e));
10781       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10782       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10783       is_removable    = (is_diggable || is_collectible);
10784
10785       can_replace[xx][yy] =
10786         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10787           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10788           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10789           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10790           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10791           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10792          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10793
10794       if (!can_replace[xx][yy])
10795         complete_replace = FALSE;
10796     }
10797
10798     if (!change->only_if_complete || complete_replace)
10799     {
10800       boolean something_has_changed = FALSE;
10801
10802       if (change->only_if_complete && change->use_random_replace &&
10803           RND(100) < change->random_percentage)
10804         return FALSE;
10805
10806       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10807       {
10808         int ex = x + xx - 1;
10809         int ey = y + yy - 1;
10810         int content_element;
10811
10812         if (can_replace[xx][yy] && (!change->use_random_replace ||
10813                                     RND(100) < change->random_percentage))
10814         {
10815           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10816             RemoveMovingField(ex, ey);
10817
10818           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10819
10820           content_element = change->target_content.e[xx][yy];
10821           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10822                                               ce_value, ce_score);
10823
10824           CreateElementFromChange(ex, ey, target_element);
10825
10826           something_has_changed = TRUE;
10827
10828           // for symmetry reasons, freeze newly created border elements
10829           if (ex != x || ey != y)
10830             Stop[ex][ey] = TRUE;        // no more moving in this frame
10831         }
10832       }
10833
10834       if (something_has_changed)
10835       {
10836         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10837         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10838       }
10839     }
10840   }
10841   else
10842   {
10843     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10844                                         ce_value, ce_score);
10845
10846     if (element == EL_DIAGONAL_GROWING ||
10847         element == EL_DIAGONAL_SHRINKING)
10848     {
10849       target_element = Store[x][y];
10850
10851       Store[x][y] = EL_EMPTY;
10852     }
10853
10854     // special case: element changes to player (and may be kept if walkable)
10855     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
10856       CreateElementFromChange(x, y, EL_EMPTY);
10857
10858     CreateElementFromChange(x, y, target_element);
10859
10860     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10861     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10862   }
10863
10864   // this uses direct change before indirect change
10865   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10866
10867   return TRUE;
10868 }
10869
10870 static void HandleElementChange(int x, int y, int page)
10871 {
10872   int element = MovingOrBlocked2Element(x, y);
10873   struct ElementInfo *ei = &element_info[element];
10874   struct ElementChangeInfo *change = &ei->change_page[page];
10875   boolean handle_action_before_change = FALSE;
10876
10877 #ifdef DEBUG
10878   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10879       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10880   {
10881     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10882           x, y, element, element_info[element].token_name);
10883     Debug("game:playing:HandleElementChange", "This should never happen!");
10884   }
10885 #endif
10886
10887   // this can happen with classic bombs on walkable, changing elements
10888   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10889   {
10890     return;
10891   }
10892
10893   if (ChangeDelay[x][y] == 0)           // initialize element change
10894   {
10895     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10896
10897     if (change->can_change)
10898     {
10899       // !!! not clear why graphic animation should be reset at all here !!!
10900       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10901       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10902
10903       /*
10904         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10905
10906         When using an animation frame delay of 1 (this only happens with
10907         "sp_zonk.moving.left/right" in the classic graphics), the default
10908         (non-moving) animation shows wrong animation frames (while the
10909         moving animation, like "sp_zonk.moving.left/right", is correct,
10910         so this graphical bug never shows up with the classic graphics).
10911         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10912         be drawn instead of the correct frames 0,1,2,3. This is caused by
10913         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10914         an element change: First when the change delay ("ChangeDelay[][]")
10915         counter has reached zero after decrementing, then a second time in
10916         the next frame (after "GfxFrame[][]" was already incremented) when
10917         "ChangeDelay[][]" is reset to the initial delay value again.
10918
10919         This causes frame 0 to be drawn twice, while the last frame won't
10920         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10921
10922         As some animations may already be cleverly designed around this bug
10923         (at least the "Snake Bite" snake tail animation does this), it cannot
10924         simply be fixed here without breaking such existing animations.
10925         Unfortunately, it cannot easily be detected if a graphics set was
10926         designed "before" or "after" the bug was fixed. As a workaround,
10927         a new graphics set option "game.graphics_engine_version" was added
10928         to be able to specify the game's major release version for which the
10929         graphics set was designed, which can then be used to decide if the
10930         bugfix should be used (version 4 and above) or not (version 3 or
10931         below, or if no version was specified at all, as with old sets).
10932
10933         (The wrong/fixed animation frames can be tested with the test level set
10934         "test_gfxframe" and level "000", which contains a specially prepared
10935         custom element at level position (x/y) == (11/9) which uses the zonk
10936         animation mentioned above. Using "game.graphics_engine_version: 4"
10937         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10938         This can also be seen from the debug output for this test element.)
10939       */
10940
10941       // when a custom element is about to change (for example by change delay),
10942       // do not reset graphic animation when the custom element is moving
10943       if (game.graphics_engine_version < 4 &&
10944           !IS_MOVING(x, y))
10945       {
10946         ResetGfxAnimation(x, y);
10947         ResetRandomAnimationValue(x, y);
10948       }
10949
10950       if (change->pre_change_function)
10951         change->pre_change_function(x, y);
10952     }
10953   }
10954
10955   ChangeDelay[x][y]--;
10956
10957   if (ChangeDelay[x][y] != 0)           // continue element change
10958   {
10959     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10960
10961     // also needed if CE can not change, but has CE delay with CE action
10962     if (IS_ANIMATED(graphic))
10963       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10964
10965     if (change->can_change)
10966     {
10967       if (change->change_function)
10968         change->change_function(x, y);
10969     }
10970   }
10971   else                                  // finish element change
10972   {
10973     if (ChangePage[x][y] != -1)         // remember page from delayed change
10974     {
10975       page = ChangePage[x][y];
10976       ChangePage[x][y] = -1;
10977
10978       change = &ei->change_page[page];
10979     }
10980
10981     if (IS_MOVING(x, y))                // never change a running system ;-)
10982     {
10983       ChangeDelay[x][y] = 1;            // try change after next move step
10984       ChangePage[x][y] = page;          // remember page to use for change
10985
10986       return;
10987     }
10988
10989     // special case: set new level random seed before changing element
10990     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10991       handle_action_before_change = TRUE;
10992
10993     if (change->has_action && handle_action_before_change)
10994       ExecuteCustomElementAction(x, y, element, page);
10995
10996     if (change->can_change)
10997     {
10998       if (ChangeElement(x, y, element, page))
10999       {
11000         if (change->post_change_function)
11001           change->post_change_function(x, y);
11002       }
11003     }
11004
11005     if (change->has_action && !handle_action_before_change)
11006       ExecuteCustomElementAction(x, y, element, page);
11007   }
11008 }
11009
11010 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11011                                               int trigger_element,
11012                                               int trigger_event,
11013                                               int trigger_player,
11014                                               int trigger_side,
11015                                               int trigger_page)
11016 {
11017   boolean change_done_any = FALSE;
11018   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11019   int i;
11020
11021   if (!(trigger_events[trigger_element][trigger_event]))
11022     return FALSE;
11023
11024   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11025
11026   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11027   {
11028     int element = EL_CUSTOM_START + i;
11029     boolean change_done = FALSE;
11030     int p;
11031
11032     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11033         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11034       continue;
11035
11036     for (p = 0; p < element_info[element].num_change_pages; p++)
11037     {
11038       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11039
11040       if (change->can_change_or_has_action &&
11041           change->has_event[trigger_event] &&
11042           change->trigger_side & trigger_side &&
11043           change->trigger_player & trigger_player &&
11044           change->trigger_page & trigger_page_bits &&
11045           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11046       {
11047         change->actual_trigger_element = trigger_element;
11048         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11049         change->actual_trigger_player_bits = trigger_player;
11050         change->actual_trigger_side = trigger_side;
11051         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11052         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11053
11054         if ((change->can_change && !change_done) || change->has_action)
11055         {
11056           int x, y;
11057
11058           SCAN_PLAYFIELD(x, y)
11059           {
11060             if (Tile[x][y] == element)
11061             {
11062               if (change->can_change && !change_done)
11063               {
11064                 // if element already changed in this frame, not only prevent
11065                 // another element change (checked in ChangeElement()), but
11066                 // also prevent additional element actions for this element
11067
11068                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11069                     !level.use_action_after_change_bug)
11070                   continue;
11071
11072                 ChangeDelay[x][y] = 1;
11073                 ChangeEvent[x][y] = trigger_event;
11074
11075                 HandleElementChange(x, y, p);
11076               }
11077               else if (change->has_action)
11078               {
11079                 // if element already changed in this frame, not only prevent
11080                 // another element change (checked in ChangeElement()), but
11081                 // also prevent additional element actions for this element
11082
11083                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11084                     !level.use_action_after_change_bug)
11085                   continue;
11086
11087                 ExecuteCustomElementAction(x, y, element, p);
11088                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11089               }
11090             }
11091           }
11092
11093           if (change->can_change)
11094           {
11095             change_done = TRUE;
11096             change_done_any = TRUE;
11097           }
11098         }
11099       }
11100     }
11101   }
11102
11103   RECURSION_LOOP_DETECTION_END();
11104
11105   return change_done_any;
11106 }
11107
11108 static boolean CheckElementChangeExt(int x, int y,
11109                                      int element,
11110                                      int trigger_element,
11111                                      int trigger_event,
11112                                      int trigger_player,
11113                                      int trigger_side)
11114 {
11115   boolean change_done = FALSE;
11116   int p;
11117
11118   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11119       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11120     return FALSE;
11121
11122   if (Tile[x][y] == EL_BLOCKED)
11123   {
11124     Blocked2Moving(x, y, &x, &y);
11125     element = Tile[x][y];
11126   }
11127
11128   // check if element has already changed or is about to change after moving
11129   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11130        Tile[x][y] != element) ||
11131
11132       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11133        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11134         ChangePage[x][y] != -1)))
11135     return FALSE;
11136
11137   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11138
11139   for (p = 0; p < element_info[element].num_change_pages; p++)
11140   {
11141     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11142
11143     /* check trigger element for all events where the element that is checked
11144        for changing interacts with a directly adjacent element -- this is
11145        different to element changes that affect other elements to change on the
11146        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11147     boolean check_trigger_element =
11148       (trigger_event == CE_NEXT_TO_X ||
11149        trigger_event == CE_TOUCHING_X ||
11150        trigger_event == CE_HITTING_X ||
11151        trigger_event == CE_HIT_BY_X ||
11152        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11153
11154     if (change->can_change_or_has_action &&
11155         change->has_event[trigger_event] &&
11156         change->trigger_side & trigger_side &&
11157         change->trigger_player & trigger_player &&
11158         (!check_trigger_element ||
11159          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11160     {
11161       change->actual_trigger_element = trigger_element;
11162       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11163       change->actual_trigger_player_bits = trigger_player;
11164       change->actual_trigger_side = trigger_side;
11165       change->actual_trigger_ce_value = CustomValue[x][y];
11166       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11167
11168       // special case: trigger element not at (x,y) position for some events
11169       if (check_trigger_element)
11170       {
11171         static struct
11172         {
11173           int dx, dy;
11174         } move_xy[] =
11175           {
11176             {  0,  0 },
11177             { -1,  0 },
11178             { +1,  0 },
11179             {  0,  0 },
11180             {  0, -1 },
11181             {  0,  0 }, { 0, 0 }, { 0, 0 },
11182             {  0, +1 }
11183           };
11184
11185         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11186         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11187
11188         change->actual_trigger_ce_value = CustomValue[xx][yy];
11189         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11190       }
11191
11192       if (change->can_change && !change_done)
11193       {
11194         ChangeDelay[x][y] = 1;
11195         ChangeEvent[x][y] = trigger_event;
11196
11197         HandleElementChange(x, y, p);
11198
11199         change_done = TRUE;
11200       }
11201       else if (change->has_action)
11202       {
11203         ExecuteCustomElementAction(x, y, element, p);
11204         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11205       }
11206     }
11207   }
11208
11209   RECURSION_LOOP_DETECTION_END();
11210
11211   return change_done;
11212 }
11213
11214 static void PlayPlayerSound(struct PlayerInfo *player)
11215 {
11216   int jx = player->jx, jy = player->jy;
11217   int sound_element = player->artwork_element;
11218   int last_action = player->last_action_waiting;
11219   int action = player->action_waiting;
11220
11221   if (player->is_waiting)
11222   {
11223     if (action != last_action)
11224       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11225     else
11226       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11227   }
11228   else
11229   {
11230     if (action != last_action)
11231       StopSound(element_info[sound_element].sound[last_action]);
11232
11233     if (last_action == ACTION_SLEEPING)
11234       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11235   }
11236 }
11237
11238 static void PlayAllPlayersSound(void)
11239 {
11240   int i;
11241
11242   for (i = 0; i < MAX_PLAYERS; i++)
11243     if (stored_player[i].active)
11244       PlayPlayerSound(&stored_player[i]);
11245 }
11246
11247 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11248 {
11249   boolean last_waiting = player->is_waiting;
11250   int move_dir = player->MovDir;
11251
11252   player->dir_waiting = move_dir;
11253   player->last_action_waiting = player->action_waiting;
11254
11255   if (is_waiting)
11256   {
11257     if (!last_waiting)          // not waiting -> waiting
11258     {
11259       player->is_waiting = TRUE;
11260
11261       player->frame_counter_bored =
11262         FrameCounter +
11263         game.player_boring_delay_fixed +
11264         GetSimpleRandom(game.player_boring_delay_random);
11265       player->frame_counter_sleeping =
11266         FrameCounter +
11267         game.player_sleeping_delay_fixed +
11268         GetSimpleRandom(game.player_sleeping_delay_random);
11269
11270       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11271     }
11272
11273     if (game.player_sleeping_delay_fixed +
11274         game.player_sleeping_delay_random > 0 &&
11275         player->anim_delay_counter == 0 &&
11276         player->post_delay_counter == 0 &&
11277         FrameCounter >= player->frame_counter_sleeping)
11278       player->is_sleeping = TRUE;
11279     else if (game.player_boring_delay_fixed +
11280              game.player_boring_delay_random > 0 &&
11281              FrameCounter >= player->frame_counter_bored)
11282       player->is_bored = TRUE;
11283
11284     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11285                               player->is_bored ? ACTION_BORING :
11286                               ACTION_WAITING);
11287
11288     if (player->is_sleeping && player->use_murphy)
11289     {
11290       // special case for sleeping Murphy when leaning against non-free tile
11291
11292       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11293           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11294            !IS_MOVING(player->jx - 1, player->jy)))
11295         move_dir = MV_LEFT;
11296       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11297                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11298                 !IS_MOVING(player->jx + 1, player->jy)))
11299         move_dir = MV_RIGHT;
11300       else
11301         player->is_sleeping = FALSE;
11302
11303       player->dir_waiting = move_dir;
11304     }
11305
11306     if (player->is_sleeping)
11307     {
11308       if (player->num_special_action_sleeping > 0)
11309       {
11310         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11311         {
11312           int last_special_action = player->special_action_sleeping;
11313           int num_special_action = player->num_special_action_sleeping;
11314           int special_action =
11315             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11316              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11317              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11318              last_special_action + 1 : ACTION_SLEEPING);
11319           int special_graphic =
11320             el_act_dir2img(player->artwork_element, special_action, move_dir);
11321
11322           player->anim_delay_counter =
11323             graphic_info[special_graphic].anim_delay_fixed +
11324             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11325           player->post_delay_counter =
11326             graphic_info[special_graphic].post_delay_fixed +
11327             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11328
11329           player->special_action_sleeping = special_action;
11330         }
11331
11332         if (player->anim_delay_counter > 0)
11333         {
11334           player->action_waiting = player->special_action_sleeping;
11335           player->anim_delay_counter--;
11336         }
11337         else if (player->post_delay_counter > 0)
11338         {
11339           player->post_delay_counter--;
11340         }
11341       }
11342     }
11343     else if (player->is_bored)
11344     {
11345       if (player->num_special_action_bored > 0)
11346       {
11347         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11348         {
11349           int special_action =
11350             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11351           int special_graphic =
11352             el_act_dir2img(player->artwork_element, special_action, move_dir);
11353
11354           player->anim_delay_counter =
11355             graphic_info[special_graphic].anim_delay_fixed +
11356             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11357           player->post_delay_counter =
11358             graphic_info[special_graphic].post_delay_fixed +
11359             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11360
11361           player->special_action_bored = special_action;
11362         }
11363
11364         if (player->anim_delay_counter > 0)
11365         {
11366           player->action_waiting = player->special_action_bored;
11367           player->anim_delay_counter--;
11368         }
11369         else if (player->post_delay_counter > 0)
11370         {
11371           player->post_delay_counter--;
11372         }
11373       }
11374     }
11375   }
11376   else if (last_waiting)        // waiting -> not waiting
11377   {
11378     player->is_waiting = FALSE;
11379     player->is_bored = FALSE;
11380     player->is_sleeping = FALSE;
11381
11382     player->frame_counter_bored = -1;
11383     player->frame_counter_sleeping = -1;
11384
11385     player->anim_delay_counter = 0;
11386     player->post_delay_counter = 0;
11387
11388     player->dir_waiting = player->MovDir;
11389     player->action_waiting = ACTION_DEFAULT;
11390
11391     player->special_action_bored = ACTION_DEFAULT;
11392     player->special_action_sleeping = ACTION_DEFAULT;
11393   }
11394 }
11395
11396 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11397 {
11398   if ((!player->is_moving  && player->was_moving) ||
11399       (player->MovPos == 0 && player->was_moving) ||
11400       (player->is_snapping && !player->was_snapping) ||
11401       (player->is_dropping && !player->was_dropping))
11402   {
11403     if (!CheckSaveEngineSnapshotToList())
11404       return;
11405
11406     player->was_moving = FALSE;
11407     player->was_snapping = TRUE;
11408     player->was_dropping = TRUE;
11409   }
11410   else
11411   {
11412     if (player->is_moving)
11413       player->was_moving = TRUE;
11414
11415     if (!player->is_snapping)
11416       player->was_snapping = FALSE;
11417
11418     if (!player->is_dropping)
11419       player->was_dropping = FALSE;
11420   }
11421
11422   static struct MouseActionInfo mouse_action_last = { 0 };
11423   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11424   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11425
11426   if (new_released)
11427     CheckSaveEngineSnapshotToList();
11428
11429   mouse_action_last = mouse_action;
11430 }
11431
11432 static void CheckSingleStepMode(struct PlayerInfo *player)
11433 {
11434   if (tape.single_step && tape.recording && !tape.pausing)
11435   {
11436     // as it is called "single step mode", just return to pause mode when the
11437     // player stopped moving after one tile (or never starts moving at all)
11438     // (reverse logic needed here in case single step mode used in team mode)
11439     if (player->is_moving ||
11440         player->is_pushing ||
11441         player->is_dropping_pressed ||
11442         player->effective_mouse_action.button)
11443       game.enter_single_step_mode = FALSE;
11444   }
11445
11446   CheckSaveEngineSnapshot(player);
11447 }
11448
11449 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11450 {
11451   int left      = player_action & JOY_LEFT;
11452   int right     = player_action & JOY_RIGHT;
11453   int up        = player_action & JOY_UP;
11454   int down      = player_action & JOY_DOWN;
11455   int button1   = player_action & JOY_BUTTON_1;
11456   int button2   = player_action & JOY_BUTTON_2;
11457   int dx        = (left ? -1 : right ? 1 : 0);
11458   int dy        = (up   ? -1 : down  ? 1 : 0);
11459
11460   if (!player->active || tape.pausing)
11461     return 0;
11462
11463   if (player_action)
11464   {
11465     if (button1)
11466       SnapField(player, dx, dy);
11467     else
11468     {
11469       if (button2)
11470         DropElement(player);
11471
11472       MovePlayer(player, dx, dy);
11473     }
11474
11475     CheckSingleStepMode(player);
11476
11477     SetPlayerWaiting(player, FALSE);
11478
11479     return player_action;
11480   }
11481   else
11482   {
11483     // no actions for this player (no input at player's configured device)
11484
11485     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11486     SnapField(player, 0, 0);
11487     CheckGravityMovementWhenNotMoving(player);
11488
11489     if (player->MovPos == 0)
11490       SetPlayerWaiting(player, TRUE);
11491
11492     if (player->MovPos == 0)    // needed for tape.playing
11493       player->is_moving = FALSE;
11494
11495     player->is_dropping = FALSE;
11496     player->is_dropping_pressed = FALSE;
11497     player->drop_pressed_delay = 0;
11498
11499     CheckSingleStepMode(player);
11500
11501     return 0;
11502   }
11503 }
11504
11505 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11506                                          byte *tape_action)
11507 {
11508   if (!tape.use_mouse_actions)
11509     return;
11510
11511   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11512   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11513   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11514 }
11515
11516 static void SetTapeActionFromMouseAction(byte *tape_action,
11517                                          struct MouseActionInfo *mouse_action)
11518 {
11519   if (!tape.use_mouse_actions)
11520     return;
11521
11522   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11523   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11524   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11525 }
11526
11527 static void CheckLevelSolved(void)
11528 {
11529   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11530   {
11531     if (game_em.level_solved &&
11532         !game_em.game_over)                             // game won
11533     {
11534       LevelSolved();
11535
11536       game_em.game_over = TRUE;
11537
11538       game.all_players_gone = TRUE;
11539     }
11540
11541     if (game_em.game_over)                              // game lost
11542       game.all_players_gone = TRUE;
11543   }
11544   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11545   {
11546     if (game_sp.level_solved &&
11547         !game_sp.game_over)                             // game won
11548     {
11549       LevelSolved();
11550
11551       game_sp.game_over = TRUE;
11552
11553       game.all_players_gone = TRUE;
11554     }
11555
11556     if (game_sp.game_over)                              // game lost
11557       game.all_players_gone = TRUE;
11558   }
11559   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11560   {
11561     if (game_mm.level_solved &&
11562         !game_mm.game_over)                             // game won
11563     {
11564       LevelSolved();
11565
11566       game_mm.game_over = TRUE;
11567
11568       game.all_players_gone = TRUE;
11569     }
11570
11571     if (game_mm.game_over)                              // game lost
11572       game.all_players_gone = TRUE;
11573   }
11574 }
11575
11576 static void CheckLevelTime_StepCounter(void)
11577 {
11578   int i;
11579
11580   TimePlayed++;
11581
11582   if (TimeLeft > 0)
11583   {
11584     TimeLeft--;
11585
11586     if (TimeLeft <= 10 && game.time_limit && !game.LevelSolved)
11587       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11588
11589     game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11590
11591     DisplayGameControlValues();
11592
11593     if (!TimeLeft && game.time_limit && !game.LevelSolved)
11594       for (i = 0; i < MAX_PLAYERS; i++)
11595         KillPlayer(&stored_player[i]);
11596   }
11597   else if (game.no_level_time_limit && !game.all_players_gone)
11598   {
11599     game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11600
11601     DisplayGameControlValues();
11602   }
11603 }
11604
11605 static void CheckLevelTime(void)
11606 {
11607   int i;
11608
11609   if (TimeFrames >= FRAMES_PER_SECOND)
11610   {
11611     TimeFrames = 0;
11612     TapeTime++;
11613
11614     for (i = 0; i < MAX_PLAYERS; i++)
11615     {
11616       struct PlayerInfo *player = &stored_player[i];
11617
11618       if (SHIELD_ON(player))
11619       {
11620         player->shield_normal_time_left--;
11621
11622         if (player->shield_deadly_time_left > 0)
11623           player->shield_deadly_time_left--;
11624       }
11625     }
11626
11627     if (!game.LevelSolved && !level.use_step_counter)
11628     {
11629       TimePlayed++;
11630
11631       if (TimeLeft > 0)
11632       {
11633         TimeLeft--;
11634
11635         if (TimeLeft <= 10 && game.time_limit)
11636           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11637
11638         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11639            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11640
11641         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11642
11643         if (!TimeLeft && game.time_limit)
11644         {
11645           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11646             game_em.lev->killed_out_of_time = TRUE;
11647           else
11648             for (i = 0; i < MAX_PLAYERS; i++)
11649               KillPlayer(&stored_player[i]);
11650         }
11651       }
11652       else if (game.no_level_time_limit && !game.all_players_gone)
11653       {
11654         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11655       }
11656
11657       game_em.lev->time = (game.no_level_time_limit ? TimePlayed : TimeLeft);
11658     }
11659
11660     if (tape.recording || tape.playing)
11661       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11662   }
11663
11664   if (tape.recording || tape.playing)
11665     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11666
11667   UpdateAndDisplayGameControlValues();
11668 }
11669
11670 void AdvanceFrameAndPlayerCounters(int player_nr)
11671 {
11672   int i;
11673
11674   // advance frame counters (global frame counter and time frame counter)
11675   FrameCounter++;
11676   TimeFrames++;
11677
11678   // advance player counters (counters for move delay, move animation etc.)
11679   for (i = 0; i < MAX_PLAYERS; i++)
11680   {
11681     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11682     int move_delay_value = stored_player[i].move_delay_value;
11683     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11684
11685     if (!advance_player_counters)       // not all players may be affected
11686       continue;
11687
11688     if (move_frames == 0)       // less than one move per game frame
11689     {
11690       int stepsize = TILEX / move_delay_value;
11691       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11692       int count = (stored_player[i].is_moving ?
11693                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11694
11695       if (count % delay == 0)
11696         move_frames = 1;
11697     }
11698
11699     stored_player[i].Frame += move_frames;
11700
11701     if (stored_player[i].MovPos != 0)
11702       stored_player[i].StepFrame += move_frames;
11703
11704     if (stored_player[i].move_delay > 0)
11705       stored_player[i].move_delay--;
11706
11707     // due to bugs in previous versions, counter must count up, not down
11708     if (stored_player[i].push_delay != -1)
11709       stored_player[i].push_delay++;
11710
11711     if (stored_player[i].drop_delay > 0)
11712       stored_player[i].drop_delay--;
11713
11714     if (stored_player[i].is_dropping_pressed)
11715       stored_player[i].drop_pressed_delay++;
11716   }
11717 }
11718
11719 void AdvanceFrameCounter(void)
11720 {
11721   FrameCounter++;
11722 }
11723
11724 void AdvanceGfxFrame(void)
11725 {
11726   int x, y;
11727
11728   SCAN_PLAYFIELD(x, y)
11729   {
11730     GfxFrame[x][y]++;
11731   }
11732 }
11733
11734 static void HandleMouseAction(struct MouseActionInfo *mouse_action,
11735                               struct MouseActionInfo *mouse_action_last)
11736 {
11737   if (mouse_action->button)
11738   {
11739     int new_button = (mouse_action->button && mouse_action_last->button == 0);
11740     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action->button);
11741     int x = mouse_action->lx;
11742     int y = mouse_action->ly;
11743     int element = Tile[x][y];
11744
11745     if (new_button)
11746     {
11747       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
11748       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
11749                                          ch_button);
11750     }
11751
11752     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
11753     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
11754                                        ch_button);
11755
11756     if (level.use_step_counter)
11757     {
11758       boolean counted_click = FALSE;
11759
11760       // element clicked that can change when clicked/pressed
11761       if (CAN_CHANGE_OR_HAS_ACTION(element) &&
11762           (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
11763            HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE)))
11764         counted_click = TRUE;
11765
11766       // element clicked that can trigger change when clicked/pressed
11767       if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] ||
11768           trigger_events[element][CE_MOUSE_PRESSED_ON_X])
11769         counted_click = TRUE;
11770
11771       if (new_button && counted_click)
11772         CheckLevelTime_StepCounter();
11773     }
11774   }
11775 }
11776
11777 void StartGameActions(boolean init_network_game, boolean record_tape,
11778                       int random_seed)
11779 {
11780   unsigned int new_random_seed = InitRND(random_seed);
11781
11782   if (record_tape)
11783     TapeStartRecording(new_random_seed);
11784
11785   if (setup.auto_pause_on_start && !tape.pausing)
11786     TapeTogglePause(TAPE_TOGGLE_MANUAL);
11787
11788   if (init_network_game)
11789   {
11790     SendToServer_LevelFile();
11791     SendToServer_StartPlaying();
11792
11793     return;
11794   }
11795
11796   InitGame();
11797 }
11798
11799 static void GameActionsExt(void)
11800 {
11801 #if 0
11802   static unsigned int game_frame_delay = 0;
11803 #endif
11804   unsigned int game_frame_delay_value;
11805   byte *recorded_player_action;
11806   byte summarized_player_action = 0;
11807   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11808   int i;
11809
11810   // detect endless loops, caused by custom element programming
11811   if (recursion_loop_detected && recursion_loop_depth == 0)
11812   {
11813     char *message = getStringCat3("Internal Error! Element ",
11814                                   EL_NAME(recursion_loop_element),
11815                                   " caused endless loop! Quit the game?");
11816
11817     Warn("element '%s' caused endless loop in game engine",
11818          EL_NAME(recursion_loop_element));
11819
11820     RequestQuitGameExt(program.headless, level_editor_test_game, message);
11821
11822     recursion_loop_detected = FALSE;    // if game should be continued
11823
11824     free(message);
11825
11826     return;
11827   }
11828
11829   if (game.restart_level)
11830     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11831
11832   CheckLevelSolved();
11833
11834   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11835     GameWon();
11836
11837   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11838     TapeStop();
11839
11840   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11841     return;
11842
11843   game_frame_delay_value =
11844     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11845
11846   if (tape.playing && tape.warp_forward && !tape.pausing)
11847     game_frame_delay_value = 0;
11848
11849   SetVideoFrameDelay(game_frame_delay_value);
11850
11851   // (de)activate virtual buttons depending on current game status
11852   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11853   {
11854     if (game.all_players_gone)  // if no players there to be controlled anymore
11855       SetOverlayActive(FALSE);
11856     else if (!tape.playing)     // if game continues after tape stopped playing
11857       SetOverlayActive(TRUE);
11858   }
11859
11860 #if 0
11861 #if 0
11862   // ---------- main game synchronization point ----------
11863
11864   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11865
11866   Debug("game:playing:skip", "skip == %d", skip);
11867
11868 #else
11869   // ---------- main game synchronization point ----------
11870
11871   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11872 #endif
11873 #endif
11874
11875   if (network_playing && !network_player_action_received)
11876   {
11877     // try to get network player actions in time
11878
11879     // last chance to get network player actions without main loop delay
11880     HandleNetworking();
11881
11882     // game was quit by network peer
11883     if (game_status != GAME_MODE_PLAYING)
11884       return;
11885
11886     // check if network player actions still missing and game still running
11887     if (!network_player_action_received && !checkGameEnded())
11888       return;           // failed to get network player actions in time
11889
11890     // do not yet reset "network_player_action_received" (for tape.pausing)
11891   }
11892
11893   if (tape.pausing)
11894     return;
11895
11896   // at this point we know that we really continue executing the game
11897
11898   network_player_action_received = FALSE;
11899
11900   // when playing tape, read previously recorded player input from tape data
11901   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11902
11903   local_player->effective_mouse_action = local_player->mouse_action;
11904
11905   if (recorded_player_action != NULL)
11906     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11907                                  recorded_player_action);
11908
11909   // TapePlayAction() may return NULL when toggling to "pause before death"
11910   if (tape.pausing)
11911     return;
11912
11913   if (tape.set_centered_player)
11914   {
11915     game.centered_player_nr_next = tape.centered_player_nr_next;
11916     game.set_centered_player = TRUE;
11917   }
11918
11919   for (i = 0; i < MAX_PLAYERS; i++)
11920   {
11921     summarized_player_action |= stored_player[i].action;
11922
11923     if (!network_playing && (game.team_mode || tape.playing))
11924       stored_player[i].effective_action = stored_player[i].action;
11925   }
11926
11927   if (network_playing && !checkGameEnded())
11928     SendToServer_MovePlayer(summarized_player_action);
11929
11930   // summarize all actions at local players mapped input device position
11931   // (this allows using different input devices in single player mode)
11932   if (!network.enabled && !game.team_mode)
11933     stored_player[map_player_action[local_player->index_nr]].effective_action =
11934       summarized_player_action;
11935
11936   // summarize all actions at centered player in local team mode
11937   if (tape.recording &&
11938       setup.team_mode && !network.enabled &&
11939       setup.input_on_focus &&
11940       game.centered_player_nr != -1)
11941   {
11942     for (i = 0; i < MAX_PLAYERS; i++)
11943       stored_player[map_player_action[i]].effective_action =
11944         (i == game.centered_player_nr ? summarized_player_action : 0);
11945   }
11946
11947   if (recorded_player_action != NULL)
11948     for (i = 0; i < MAX_PLAYERS; i++)
11949       stored_player[i].effective_action = recorded_player_action[i];
11950
11951   for (i = 0; i < MAX_PLAYERS; i++)
11952   {
11953     tape_action[i] = stored_player[i].effective_action;
11954
11955     /* (this may happen in the RND game engine if a player was not present on
11956        the playfield on level start, but appeared later from a custom element */
11957     if (setup.team_mode &&
11958         tape.recording &&
11959         tape_action[i] &&
11960         !tape.player_participates[i])
11961       tape.player_participates[i] = TRUE;
11962   }
11963
11964   SetTapeActionFromMouseAction(tape_action,
11965                                &local_player->effective_mouse_action);
11966
11967   // only record actions from input devices, but not programmed actions
11968   if (tape.recording)
11969     TapeRecordAction(tape_action);
11970
11971   // remember if game was played (especially after tape stopped playing)
11972   if (!tape.playing && summarized_player_action && !checkGameFailed())
11973     game.GamePlayed = TRUE;
11974
11975 #if USE_NEW_PLAYER_ASSIGNMENTS
11976   // !!! also map player actions in single player mode !!!
11977   // if (game.team_mode)
11978   if (1)
11979   {
11980     byte mapped_action[MAX_PLAYERS];
11981
11982 #if DEBUG_PLAYER_ACTIONS
11983     for (i = 0; i < MAX_PLAYERS; i++)
11984       DebugContinued("", "%d, ", stored_player[i].effective_action);
11985 #endif
11986
11987     for (i = 0; i < MAX_PLAYERS; i++)
11988       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11989
11990     for (i = 0; i < MAX_PLAYERS; i++)
11991       stored_player[i].effective_action = mapped_action[i];
11992
11993 #if DEBUG_PLAYER_ACTIONS
11994     DebugContinued("", "=> ");
11995     for (i = 0; i < MAX_PLAYERS; i++)
11996       DebugContinued("", "%d, ", stored_player[i].effective_action);
11997     DebugContinued("game:playing:player", "\n");
11998 #endif
11999   }
12000 #if DEBUG_PLAYER_ACTIONS
12001   else
12002   {
12003     for (i = 0; i < MAX_PLAYERS; i++)
12004       DebugContinued("", "%d, ", stored_player[i].effective_action);
12005     DebugContinued("game:playing:player", "\n");
12006   }
12007 #endif
12008 #endif
12009
12010   for (i = 0; i < MAX_PLAYERS; i++)
12011   {
12012     // allow engine snapshot in case of changed movement attempt
12013     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
12014         (stored_player[i].effective_action & KEY_MOTION))
12015       game.snapshot.changed_action = TRUE;
12016
12017     // allow engine snapshot in case of snapping/dropping attempt
12018     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
12019         (stored_player[i].effective_action & KEY_BUTTON) != 0)
12020       game.snapshot.changed_action = TRUE;
12021
12022     game.snapshot.last_action[i] = stored_player[i].effective_action;
12023   }
12024
12025   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12026   {
12027     GameActions_EM_Main();
12028   }
12029   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12030   {
12031     GameActions_SP_Main();
12032   }
12033   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
12034   {
12035     GameActions_MM_Main();
12036   }
12037   else
12038   {
12039     GameActions_RND_Main();
12040   }
12041
12042   BlitScreenToBitmap(backbuffer);
12043
12044   CheckLevelSolved();
12045   CheckLevelTime();
12046
12047   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
12048
12049   if (global.show_frames_per_second)
12050   {
12051     static unsigned int fps_counter = 0;
12052     static int fps_frames = 0;
12053     unsigned int fps_delay_ms = Counter() - fps_counter;
12054
12055     fps_frames++;
12056
12057     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
12058     {
12059       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12060
12061       fps_frames = 0;
12062       fps_counter = Counter();
12063
12064       // always draw FPS to screen after FPS value was updated
12065       redraw_mask |= REDRAW_FPS;
12066     }
12067
12068     // only draw FPS if no screen areas are deactivated (invisible warp mode)
12069     if (GetDrawDeactivationMask() == REDRAW_NONE)
12070       redraw_mask |= REDRAW_FPS;
12071   }
12072 }
12073
12074 static void GameActions_CheckSaveEngineSnapshot(void)
12075 {
12076   if (!game.snapshot.save_snapshot)
12077     return;
12078
12079   // clear flag for saving snapshot _before_ saving snapshot
12080   game.snapshot.save_snapshot = FALSE;
12081
12082   SaveEngineSnapshotToList();
12083 }
12084
12085 void GameActions(void)
12086 {
12087   GameActionsExt();
12088
12089   GameActions_CheckSaveEngineSnapshot();
12090 }
12091
12092 void GameActions_EM_Main(void)
12093 {
12094   byte effective_action[MAX_PLAYERS];
12095   int i;
12096
12097   for (i = 0; i < MAX_PLAYERS; i++)
12098     effective_action[i] = stored_player[i].effective_action;
12099
12100   GameActions_EM(effective_action);
12101 }
12102
12103 void GameActions_SP_Main(void)
12104 {
12105   byte effective_action[MAX_PLAYERS];
12106   int i;
12107
12108   for (i = 0; i < MAX_PLAYERS; i++)
12109     effective_action[i] = stored_player[i].effective_action;
12110
12111   GameActions_SP(effective_action);
12112
12113   for (i = 0; i < MAX_PLAYERS; i++)
12114   {
12115     if (stored_player[i].force_dropping)
12116       stored_player[i].action |= KEY_BUTTON_DROP;
12117
12118     stored_player[i].force_dropping = FALSE;
12119   }
12120 }
12121
12122 void GameActions_MM_Main(void)
12123 {
12124   AdvanceGfxFrame();
12125
12126   GameActions_MM(local_player->effective_mouse_action);
12127 }
12128
12129 void GameActions_RND_Main(void)
12130 {
12131   GameActions_RND();
12132 }
12133
12134 void GameActions_RND(void)
12135 {
12136   static struct MouseActionInfo mouse_action_last = { 0 };
12137   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12138   int magic_wall_x = 0, magic_wall_y = 0;
12139   int i, x, y, element, graphic, last_gfx_frame;
12140
12141   InitPlayfieldScanModeVars();
12142
12143   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12144   {
12145     SCAN_PLAYFIELD(x, y)
12146     {
12147       ChangeCount[x][y] = 0;
12148       ChangeEvent[x][y] = -1;
12149     }
12150   }
12151
12152   if (game.set_centered_player)
12153   {
12154     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12155
12156     // switching to "all players" only possible if all players fit to screen
12157     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12158     {
12159       game.centered_player_nr_next = game.centered_player_nr;
12160       game.set_centered_player = FALSE;
12161     }
12162
12163     // do not switch focus to non-existing (or non-active) player
12164     if (game.centered_player_nr_next >= 0 &&
12165         !stored_player[game.centered_player_nr_next].active)
12166     {
12167       game.centered_player_nr_next = game.centered_player_nr;
12168       game.set_centered_player = FALSE;
12169     }
12170   }
12171
12172   if (game.set_centered_player &&
12173       ScreenMovPos == 0)        // screen currently aligned at tile position
12174   {
12175     int sx, sy;
12176
12177     if (game.centered_player_nr_next == -1)
12178     {
12179       setScreenCenteredToAllPlayers(&sx, &sy);
12180     }
12181     else
12182     {
12183       sx = stored_player[game.centered_player_nr_next].jx;
12184       sy = stored_player[game.centered_player_nr_next].jy;
12185     }
12186
12187     game.centered_player_nr = game.centered_player_nr_next;
12188     game.set_centered_player = FALSE;
12189
12190     DrawRelocateScreen(0, 0, sx, sy, TRUE, setup.quick_switch);
12191     DrawGameDoorValues();
12192   }
12193
12194   // check single step mode (set flag and clear again if any player is active)
12195   game.enter_single_step_mode =
12196     (tape.single_step && tape.recording && !tape.pausing);
12197
12198   for (i = 0; i < MAX_PLAYERS; i++)
12199   {
12200     int actual_player_action = stored_player[i].effective_action;
12201
12202 #if 1
12203     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12204        - rnd_equinox_tetrachloride 048
12205        - rnd_equinox_tetrachloride_ii 096
12206        - rnd_emanuel_schmieg 002
12207        - doctor_sloan_ww 001, 020
12208     */
12209     if (stored_player[i].MovPos == 0)
12210       CheckGravityMovement(&stored_player[i]);
12211 #endif
12212
12213     // overwrite programmed action with tape action
12214     if (stored_player[i].programmed_action)
12215       actual_player_action = stored_player[i].programmed_action;
12216
12217     PlayerActions(&stored_player[i], actual_player_action);
12218
12219     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12220   }
12221
12222   // single step pause mode may already have been toggled by "ScrollPlayer()"
12223   if (game.enter_single_step_mode && !tape.pausing)
12224     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12225
12226   ScrollScreen(NULL, SCROLL_GO_ON);
12227
12228   /* for backwards compatibility, the following code emulates a fixed bug that
12229      occured when pushing elements (causing elements that just made their last
12230      pushing step to already (if possible) make their first falling step in the
12231      same game frame, which is bad); this code is also needed to use the famous
12232      "spring push bug" which is used in older levels and might be wanted to be
12233      used also in newer levels, but in this case the buggy pushing code is only
12234      affecting the "spring" element and no other elements */
12235
12236   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12237   {
12238     for (i = 0; i < MAX_PLAYERS; i++)
12239     {
12240       struct PlayerInfo *player = &stored_player[i];
12241       int x = player->jx;
12242       int y = player->jy;
12243
12244       if (player->active && player->is_pushing && player->is_moving &&
12245           IS_MOVING(x, y) &&
12246           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12247            Tile[x][y] == EL_SPRING))
12248       {
12249         ContinueMoving(x, y);
12250
12251         // continue moving after pushing (this is actually a bug)
12252         if (!IS_MOVING(x, y))
12253           Stop[x][y] = FALSE;
12254       }
12255     }
12256   }
12257
12258   SCAN_PLAYFIELD(x, y)
12259   {
12260     Last[x][y] = Tile[x][y];
12261
12262     ChangeCount[x][y] = 0;
12263     ChangeEvent[x][y] = -1;
12264
12265     // this must be handled before main playfield loop
12266     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12267     {
12268       MovDelay[x][y]--;
12269       if (MovDelay[x][y] <= 0)
12270         RemoveField(x, y);
12271     }
12272
12273     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12274     {
12275       MovDelay[x][y]--;
12276       if (MovDelay[x][y] <= 0)
12277       {
12278         int element = Store[x][y];
12279         int move_direction = MovDir[x][y];
12280         int player_index_bit = Store2[x][y];
12281
12282         Store[x][y] = 0;
12283         Store2[x][y] = 0;
12284
12285         RemoveField(x, y);
12286         TEST_DrawLevelField(x, y);
12287
12288         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12289
12290         if (IS_ENVELOPE(element))
12291           local_player->show_envelope = element;
12292       }
12293     }
12294
12295 #if DEBUG
12296     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12297     {
12298       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12299             x, y);
12300       Debug("game:playing:GameActions_RND", "This should never happen!");
12301
12302       ChangePage[x][y] = -1;
12303     }
12304 #endif
12305
12306     Stop[x][y] = FALSE;
12307     if (WasJustMoving[x][y] > 0)
12308       WasJustMoving[x][y]--;
12309     if (WasJustFalling[x][y] > 0)
12310       WasJustFalling[x][y]--;
12311     if (CheckCollision[x][y] > 0)
12312       CheckCollision[x][y]--;
12313     if (CheckImpact[x][y] > 0)
12314       CheckImpact[x][y]--;
12315
12316     GfxFrame[x][y]++;
12317
12318     /* reset finished pushing action (not done in ContinueMoving() to allow
12319        continuous pushing animation for elements with zero push delay) */
12320     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12321     {
12322       ResetGfxAnimation(x, y);
12323       TEST_DrawLevelField(x, y);
12324     }
12325
12326 #if DEBUG
12327     if (IS_BLOCKED(x, y))
12328     {
12329       int oldx, oldy;
12330
12331       Blocked2Moving(x, y, &oldx, &oldy);
12332       if (!IS_MOVING(oldx, oldy))
12333       {
12334         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12335         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12336         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12337         Debug("game:playing:GameActions_RND", "This should never happen!");
12338       }
12339     }
12340 #endif
12341   }
12342
12343   HandleMouseAction(&mouse_action, &mouse_action_last);
12344
12345   SCAN_PLAYFIELD(x, y)
12346   {
12347     element = Tile[x][y];
12348     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12349     last_gfx_frame = GfxFrame[x][y];
12350
12351     if (element == EL_EMPTY)
12352       graphic = el2img(GfxElementEmpty[x][y]);
12353
12354     ResetGfxFrame(x, y);
12355
12356     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12357       DrawLevelGraphicAnimation(x, y, graphic);
12358
12359     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12360         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12361       ResetRandomAnimationValue(x, y);
12362
12363     SetRandomAnimationValue(x, y);
12364
12365     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12366
12367     if (IS_INACTIVE(element))
12368     {
12369       if (IS_ANIMATED(graphic))
12370         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12371
12372       continue;
12373     }
12374
12375     // this may take place after moving, so 'element' may have changed
12376     if (IS_CHANGING(x, y) &&
12377         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12378     {
12379       int page = element_info[element].event_page_nr[CE_DELAY];
12380
12381       HandleElementChange(x, y, page);
12382
12383       element = Tile[x][y];
12384       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12385     }
12386
12387     CheckNextToConditions(x, y);
12388
12389     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12390     {
12391       StartMoving(x, y);
12392
12393       element = Tile[x][y];
12394       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12395
12396       if (IS_ANIMATED(graphic) &&
12397           !IS_MOVING(x, y) &&
12398           !Stop[x][y])
12399         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12400
12401       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12402         TEST_DrawTwinkleOnField(x, y);
12403     }
12404     else if (element == EL_ACID)
12405     {
12406       if (!Stop[x][y])
12407         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12408     }
12409     else if ((element == EL_EXIT_OPEN ||
12410               element == EL_EM_EXIT_OPEN ||
12411               element == EL_SP_EXIT_OPEN ||
12412               element == EL_STEEL_EXIT_OPEN ||
12413               element == EL_EM_STEEL_EXIT_OPEN ||
12414               element == EL_SP_TERMINAL ||
12415               element == EL_SP_TERMINAL_ACTIVE ||
12416               element == EL_EXTRA_TIME ||
12417               element == EL_SHIELD_NORMAL ||
12418               element == EL_SHIELD_DEADLY) &&
12419              IS_ANIMATED(graphic))
12420       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12421     else if (IS_MOVING(x, y))
12422       ContinueMoving(x, y);
12423     else if (IS_ACTIVE_BOMB(element))
12424       CheckDynamite(x, y);
12425     else if (element == EL_AMOEBA_GROWING)
12426       AmoebaGrowing(x, y);
12427     else if (element == EL_AMOEBA_SHRINKING)
12428       AmoebaShrinking(x, y);
12429
12430 #if !USE_NEW_AMOEBA_CODE
12431     else if (IS_AMOEBALIVE(element))
12432       AmoebaReproduce(x, y);
12433 #endif
12434
12435     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12436       Life(x, y);
12437     else if (element == EL_EXIT_CLOSED)
12438       CheckExit(x, y);
12439     else if (element == EL_EM_EXIT_CLOSED)
12440       CheckExitEM(x, y);
12441     else if (element == EL_STEEL_EXIT_CLOSED)
12442       CheckExitSteel(x, y);
12443     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12444       CheckExitSteelEM(x, y);
12445     else if (element == EL_SP_EXIT_CLOSED)
12446       CheckExitSP(x, y);
12447     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12448              element == EL_EXPANDABLE_STEELWALL_GROWING)
12449       WallGrowing(x, y);
12450     else if (element == EL_EXPANDABLE_WALL ||
12451              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12452              element == EL_EXPANDABLE_WALL_VERTICAL ||
12453              element == EL_EXPANDABLE_WALL_ANY ||
12454              element == EL_BD_EXPANDABLE_WALL ||
12455              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12456              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12457              element == EL_EXPANDABLE_STEELWALL_ANY)
12458       CheckWallGrowing(x, y);
12459     else if (element == EL_FLAMES)
12460       CheckForDragon(x, y);
12461     else if (element == EL_EXPLOSION)
12462       ; // drawing of correct explosion animation is handled separately
12463     else if (element == EL_ELEMENT_SNAPPING ||
12464              element == EL_DIAGONAL_SHRINKING ||
12465              element == EL_DIAGONAL_GROWING)
12466     {
12467       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y], GfxDir[x][y]);
12468
12469       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12470     }
12471     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12472       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12473
12474     if (IS_BELT_ACTIVE(element))
12475       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12476
12477     if (game.magic_wall_active)
12478     {
12479       int jx = local_player->jx, jy = local_player->jy;
12480
12481       // play the element sound at the position nearest to the player
12482       if ((element == EL_MAGIC_WALL_FULL ||
12483            element == EL_MAGIC_WALL_ACTIVE ||
12484            element == EL_MAGIC_WALL_EMPTYING ||
12485            element == EL_BD_MAGIC_WALL_FULL ||
12486            element == EL_BD_MAGIC_WALL_ACTIVE ||
12487            element == EL_BD_MAGIC_WALL_EMPTYING ||
12488            element == EL_DC_MAGIC_WALL_FULL ||
12489            element == EL_DC_MAGIC_WALL_ACTIVE ||
12490            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12491           ABS(x - jx) + ABS(y - jy) <
12492           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12493       {
12494         magic_wall_x = x;
12495         magic_wall_y = y;
12496       }
12497     }
12498   }
12499
12500 #if USE_NEW_AMOEBA_CODE
12501   // new experimental amoeba growth stuff
12502   if (!(FrameCounter % 8))
12503   {
12504     static unsigned int random = 1684108901;
12505
12506     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12507     {
12508       x = RND(lev_fieldx);
12509       y = RND(lev_fieldy);
12510       element = Tile[x][y];
12511
12512       if (!IS_PLAYER(x, y) &&
12513           (element == EL_EMPTY ||
12514            CAN_GROW_INTO(element) ||
12515            element == EL_QUICKSAND_EMPTY ||
12516            element == EL_QUICKSAND_FAST_EMPTY ||
12517            element == EL_ACID_SPLASH_LEFT ||
12518            element == EL_ACID_SPLASH_RIGHT))
12519       {
12520         if ((IN_LEV_FIELD(x, y - 1) && Tile[x][y - 1] == EL_AMOEBA_WET) ||
12521             (IN_LEV_FIELD(x - 1, y) && Tile[x - 1][y] == EL_AMOEBA_WET) ||
12522             (IN_LEV_FIELD(x + 1, y) && Tile[x + 1][y] == EL_AMOEBA_WET) ||
12523             (IN_LEV_FIELD(x, y + 1) && Tile[x][y + 1] == EL_AMOEBA_WET))
12524           Tile[x][y] = EL_AMOEBA_DROP;
12525       }
12526
12527       random = random * 129 + 1;
12528     }
12529   }
12530 #endif
12531
12532   game.explosions_delayed = FALSE;
12533
12534   SCAN_PLAYFIELD(x, y)
12535   {
12536     element = Tile[x][y];
12537
12538     if (ExplodeField[x][y])
12539       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12540     else if (element == EL_EXPLOSION)
12541       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12542
12543     ExplodeField[x][y] = EX_TYPE_NONE;
12544   }
12545
12546   game.explosions_delayed = TRUE;
12547
12548   if (game.magic_wall_active)
12549   {
12550     if (!(game.magic_wall_time_left % 4))
12551     {
12552       int element = Tile[magic_wall_x][magic_wall_y];
12553
12554       if (element == EL_BD_MAGIC_WALL_FULL ||
12555           element == EL_BD_MAGIC_WALL_ACTIVE ||
12556           element == EL_BD_MAGIC_WALL_EMPTYING)
12557         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12558       else if (element == EL_DC_MAGIC_WALL_FULL ||
12559                element == EL_DC_MAGIC_WALL_ACTIVE ||
12560                element == EL_DC_MAGIC_WALL_EMPTYING)
12561         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12562       else
12563         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12564     }
12565
12566     if (game.magic_wall_time_left > 0)
12567     {
12568       game.magic_wall_time_left--;
12569
12570       if (!game.magic_wall_time_left)
12571       {
12572         SCAN_PLAYFIELD(x, y)
12573         {
12574           element = Tile[x][y];
12575
12576           if (element == EL_MAGIC_WALL_ACTIVE ||
12577               element == EL_MAGIC_WALL_FULL)
12578           {
12579             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12580             TEST_DrawLevelField(x, y);
12581           }
12582           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12583                    element == EL_BD_MAGIC_WALL_FULL)
12584           {
12585             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12586             TEST_DrawLevelField(x, y);
12587           }
12588           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12589                    element == EL_DC_MAGIC_WALL_FULL)
12590           {
12591             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12592             TEST_DrawLevelField(x, y);
12593           }
12594         }
12595
12596         game.magic_wall_active = FALSE;
12597       }
12598     }
12599   }
12600
12601   if (game.light_time_left > 0)
12602   {
12603     game.light_time_left--;
12604
12605     if (game.light_time_left == 0)
12606       RedrawAllLightSwitchesAndInvisibleElements();
12607   }
12608
12609   if (game.timegate_time_left > 0)
12610   {
12611     game.timegate_time_left--;
12612
12613     if (game.timegate_time_left == 0)
12614       CloseAllOpenTimegates();
12615   }
12616
12617   if (game.lenses_time_left > 0)
12618   {
12619     game.lenses_time_left--;
12620
12621     if (game.lenses_time_left == 0)
12622       RedrawAllInvisibleElementsForLenses();
12623   }
12624
12625   if (game.magnify_time_left > 0)
12626   {
12627     game.magnify_time_left--;
12628
12629     if (game.magnify_time_left == 0)
12630       RedrawAllInvisibleElementsForMagnifier();
12631   }
12632
12633   for (i = 0; i < MAX_PLAYERS; i++)
12634   {
12635     struct PlayerInfo *player = &stored_player[i];
12636
12637     if (SHIELD_ON(player))
12638     {
12639       if (player->shield_deadly_time_left)
12640         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12641       else if (player->shield_normal_time_left)
12642         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12643     }
12644   }
12645
12646 #if USE_DELAYED_GFX_REDRAW
12647   SCAN_PLAYFIELD(x, y)
12648   {
12649     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12650     {
12651       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12652          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12653
12654       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12655         DrawLevelField(x, y);
12656
12657       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12658         DrawLevelFieldCrumbled(x, y);
12659
12660       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12661         DrawLevelFieldCrumbledNeighbours(x, y);
12662
12663       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12664         DrawTwinkleOnField(x, y);
12665     }
12666
12667     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12668   }
12669 #endif
12670
12671   DrawAllPlayers();
12672   PlayAllPlayersSound();
12673
12674   for (i = 0; i < MAX_PLAYERS; i++)
12675   {
12676     struct PlayerInfo *player = &stored_player[i];
12677
12678     if (player->show_envelope != 0 && (!player->active ||
12679                                        player->MovPos == 0))
12680     {
12681       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12682
12683       player->show_envelope = 0;
12684     }
12685   }
12686
12687   // use random number generator in every frame to make it less predictable
12688   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12689     RND(1);
12690
12691   mouse_action_last = mouse_action;
12692 }
12693
12694 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12695 {
12696   int min_x = x, min_y = y, max_x = x, max_y = y;
12697   int scr_fieldx = getScreenFieldSizeX();
12698   int scr_fieldy = getScreenFieldSizeY();
12699   int i;
12700
12701   for (i = 0; i < MAX_PLAYERS; i++)
12702   {
12703     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12704
12705     if (!stored_player[i].active || &stored_player[i] == player)
12706       continue;
12707
12708     min_x = MIN(min_x, jx);
12709     min_y = MIN(min_y, jy);
12710     max_x = MAX(max_x, jx);
12711     max_y = MAX(max_y, jy);
12712   }
12713
12714   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12715 }
12716
12717 static boolean AllPlayersInVisibleScreen(void)
12718 {
12719   int i;
12720
12721   for (i = 0; i < MAX_PLAYERS; i++)
12722   {
12723     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12724
12725     if (!stored_player[i].active)
12726       continue;
12727
12728     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12729       return FALSE;
12730   }
12731
12732   return TRUE;
12733 }
12734
12735 void ScrollLevel(int dx, int dy)
12736 {
12737   int scroll_offset = 2 * TILEX_VAR;
12738   int x, y;
12739
12740   BlitBitmap(drawto_field, drawto_field,
12741              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12742              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12743              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12744              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12745              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12746              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12747
12748   if (dx != 0)
12749   {
12750     x = (dx == 1 ? BX1 : BX2);
12751     for (y = BY1; y <= BY2; y++)
12752       DrawScreenField(x, y);
12753   }
12754
12755   if (dy != 0)
12756   {
12757     y = (dy == 1 ? BY1 : BY2);
12758     for (x = BX1; x <= BX2; x++)
12759       DrawScreenField(x, y);
12760   }
12761
12762   redraw_mask |= REDRAW_FIELD;
12763 }
12764
12765 static boolean canFallDown(struct PlayerInfo *player)
12766 {
12767   int jx = player->jx, jy = player->jy;
12768
12769   return (IN_LEV_FIELD(jx, jy + 1) &&
12770           (IS_FREE(jx, jy + 1) ||
12771            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12772           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12773           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12774 }
12775
12776 static boolean canPassField(int x, int y, int move_dir)
12777 {
12778   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12779   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12780   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12781   int nextx = x + dx;
12782   int nexty = y + dy;
12783   int element = Tile[x][y];
12784
12785   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12786           !CAN_MOVE(element) &&
12787           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12788           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12789           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12790 }
12791
12792 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12793 {
12794   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12795   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12796   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12797   int newx = x + dx;
12798   int newy = y + dy;
12799
12800   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12801           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12802           (IS_DIGGABLE(Tile[newx][newy]) ||
12803            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12804            canPassField(newx, newy, move_dir)));
12805 }
12806
12807 static void CheckGravityMovement(struct PlayerInfo *player)
12808 {
12809   if (player->gravity && !player->programmed_action)
12810   {
12811     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12812     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12813     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12814     int jx = player->jx, jy = player->jy;
12815     boolean player_is_moving_to_valid_field =
12816       (!player_is_snapping &&
12817        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12818         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12819     boolean player_can_fall_down = canFallDown(player);
12820
12821     if (player_can_fall_down &&
12822         !player_is_moving_to_valid_field)
12823       player->programmed_action = MV_DOWN;
12824   }
12825 }
12826
12827 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12828 {
12829   return CheckGravityMovement(player);
12830
12831   if (player->gravity && !player->programmed_action)
12832   {
12833     int jx = player->jx, jy = player->jy;
12834     boolean field_under_player_is_free =
12835       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12836     boolean player_is_standing_on_valid_field =
12837       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12838        (IS_WALKABLE(Tile[jx][jy]) &&
12839         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12840
12841     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12842       player->programmed_action = MV_DOWN;
12843   }
12844 }
12845
12846 /*
12847   MovePlayerOneStep()
12848   -----------------------------------------------------------------------------
12849   dx, dy:               direction (non-diagonal) to try to move the player to
12850   real_dx, real_dy:     direction as read from input device (can be diagonal)
12851 */
12852
12853 boolean MovePlayerOneStep(struct PlayerInfo *player,
12854                           int dx, int dy, int real_dx, int real_dy)
12855 {
12856   int jx = player->jx, jy = player->jy;
12857   int new_jx = jx + dx, new_jy = jy + dy;
12858   int can_move;
12859   boolean player_can_move = !player->cannot_move;
12860
12861   if (!player->active || (!dx && !dy))
12862     return MP_NO_ACTION;
12863
12864   player->MovDir = (dx < 0 ? MV_LEFT :
12865                     dx > 0 ? MV_RIGHT :
12866                     dy < 0 ? MV_UP :
12867                     dy > 0 ? MV_DOWN :  MV_NONE);
12868
12869   if (!IN_LEV_FIELD(new_jx, new_jy))
12870     return MP_NO_ACTION;
12871
12872   if (!player_can_move)
12873   {
12874     if (player->MovPos == 0)
12875     {
12876       player->is_moving = FALSE;
12877       player->is_digging = FALSE;
12878       player->is_collecting = FALSE;
12879       player->is_snapping = FALSE;
12880       player->is_pushing = FALSE;
12881     }
12882   }
12883
12884   if (!network.enabled && game.centered_player_nr == -1 &&
12885       !AllPlayersInSight(player, new_jx, new_jy))
12886     return MP_NO_ACTION;
12887
12888   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx, real_dy, DF_DIG);
12889   if (can_move != MP_MOVING)
12890     return can_move;
12891
12892   // check if DigField() has caused relocation of the player
12893   if (player->jx != jx || player->jy != jy)
12894     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12895
12896   StorePlayer[jx][jy] = 0;
12897   player->last_jx = jx;
12898   player->last_jy = jy;
12899   player->jx = new_jx;
12900   player->jy = new_jy;
12901   StorePlayer[new_jx][new_jy] = player->element_nr;
12902
12903   if (player->move_delay_value_next != -1)
12904   {
12905     player->move_delay_value = player->move_delay_value_next;
12906     player->move_delay_value_next = -1;
12907   }
12908
12909   player->MovPos =
12910     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12911
12912   player->step_counter++;
12913
12914   PlayerVisit[jx][jy] = FrameCounter;
12915
12916   player->is_moving = TRUE;
12917
12918 #if 1
12919   // should better be called in MovePlayer(), but this breaks some tapes
12920   ScrollPlayer(player, SCROLL_INIT);
12921 #endif
12922
12923   return MP_MOVING;
12924 }
12925
12926 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12927 {
12928   int jx = player->jx, jy = player->jy;
12929   int old_jx = jx, old_jy = jy;
12930   int moved = MP_NO_ACTION;
12931
12932   if (!player->active)
12933     return FALSE;
12934
12935   if (!dx && !dy)
12936   {
12937     if (player->MovPos == 0)
12938     {
12939       player->is_moving = FALSE;
12940       player->is_digging = FALSE;
12941       player->is_collecting = FALSE;
12942       player->is_snapping = FALSE;
12943       player->is_pushing = FALSE;
12944     }
12945
12946     return FALSE;
12947   }
12948
12949   if (player->move_delay > 0)
12950     return FALSE;
12951
12952   player->move_delay = -1;              // set to "uninitialized" value
12953
12954   // store if player is automatically moved to next field
12955   player->is_auto_moving = (player->programmed_action != MV_NONE);
12956
12957   // remove the last programmed player action
12958   player->programmed_action = 0;
12959
12960   if (player->MovPos)
12961   {
12962     // should only happen if pre-1.2 tape recordings are played
12963     // this is only for backward compatibility
12964
12965     int original_move_delay_value = player->move_delay_value;
12966
12967 #if DEBUG
12968     Debug("game:playing:MovePlayer",
12969           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12970           tape.counter);
12971 #endif
12972
12973     // scroll remaining steps with finest movement resolution
12974     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12975
12976     while (player->MovPos)
12977     {
12978       ScrollPlayer(player, SCROLL_GO_ON);
12979       ScrollScreen(NULL, SCROLL_GO_ON);
12980
12981       AdvanceFrameAndPlayerCounters(player->index_nr);
12982
12983       DrawAllPlayers();
12984       BackToFront_WithFrameDelay(0);
12985     }
12986
12987     player->move_delay_value = original_move_delay_value;
12988   }
12989
12990   player->is_active = FALSE;
12991
12992   if (player->last_move_dir & MV_HORIZONTAL)
12993   {
12994     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12995       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12996   }
12997   else
12998   {
12999     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13000       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13001   }
13002
13003   if (!moved && !player->is_active)
13004   {
13005     player->is_moving = FALSE;
13006     player->is_digging = FALSE;
13007     player->is_collecting = FALSE;
13008     player->is_snapping = FALSE;
13009     player->is_pushing = FALSE;
13010   }
13011
13012   jx = player->jx;
13013   jy = player->jy;
13014
13015   if (moved & MP_MOVING && !ScreenMovPos &&
13016       (player->index_nr == game.centered_player_nr ||
13017        game.centered_player_nr == -1))
13018   {
13019     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13020
13021     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13022     {
13023       // actual player has left the screen -- scroll in that direction
13024       if (jx != old_jx)         // player has moved horizontally
13025         scroll_x += (jx - old_jx);
13026       else                      // player has moved vertically
13027         scroll_y += (jy - old_jy);
13028     }
13029     else
13030     {
13031       int offset_raw = game.scroll_delay_value;
13032
13033       if (jx != old_jx)         // player has moved horizontally
13034       {
13035         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
13036         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
13037         int new_scroll_x = jx - MIDPOSX + offset_x;
13038
13039         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
13040             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
13041           scroll_x = new_scroll_x;
13042
13043         // don't scroll over playfield boundaries
13044         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
13045
13046         // don't scroll more than one field at a time
13047         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13048
13049         // don't scroll against the player's moving direction
13050         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13051             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13052           scroll_x = old_scroll_x;
13053       }
13054       else                      // player has moved vertically
13055       {
13056         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
13057         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
13058         int new_scroll_y = jy - MIDPOSY + offset_y;
13059
13060         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
13061             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13062           scroll_y = new_scroll_y;
13063
13064         // don't scroll over playfield boundaries
13065         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13066
13067         // don't scroll more than one field at a time
13068         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13069
13070         // don't scroll against the player's moving direction
13071         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13072             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13073           scroll_y = old_scroll_y;
13074       }
13075     }
13076
13077     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13078     {
13079       if (!network.enabled && game.centered_player_nr == -1 &&
13080           !AllPlayersInVisibleScreen())
13081       {
13082         scroll_x = old_scroll_x;
13083         scroll_y = old_scroll_y;
13084       }
13085       else
13086       {
13087         ScrollScreen(player, SCROLL_INIT);
13088         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13089       }
13090     }
13091   }
13092
13093   player->StepFrame = 0;
13094
13095   if (moved & MP_MOVING)
13096   {
13097     if (old_jx != jx && old_jy == jy)
13098       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13099     else if (old_jx == jx && old_jy != jy)
13100       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13101
13102     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13103
13104     player->last_move_dir = player->MovDir;
13105     player->is_moving = TRUE;
13106     player->is_snapping = FALSE;
13107     player->is_switching = FALSE;
13108     player->is_dropping = FALSE;
13109     player->is_dropping_pressed = FALSE;
13110     player->drop_pressed_delay = 0;
13111
13112 #if 0
13113     // should better be called here than above, but this breaks some tapes
13114     ScrollPlayer(player, SCROLL_INIT);
13115 #endif
13116   }
13117   else
13118   {
13119     CheckGravityMovementWhenNotMoving(player);
13120
13121     player->is_moving = FALSE;
13122
13123     /* at this point, the player is allowed to move, but cannot move right now
13124        (e.g. because of something blocking the way) -- ensure that the player
13125        is also allowed to move in the next frame (in old versions before 3.1.1,
13126        the player was forced to wait again for eight frames before next try) */
13127
13128     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13129       player->move_delay = 0;   // allow direct movement in the next frame
13130   }
13131
13132   if (player->move_delay == -1)         // not yet initialized by DigField()
13133     player->move_delay = player->move_delay_value;
13134
13135   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13136   {
13137     TestIfPlayerTouchesBadThing(jx, jy);
13138     TestIfPlayerTouchesCustomElement(jx, jy);
13139   }
13140
13141   if (!player->active)
13142     RemovePlayer(player);
13143
13144   return moved;
13145 }
13146
13147 void ScrollPlayer(struct PlayerInfo *player, int mode)
13148 {
13149   int jx = player->jx, jy = player->jy;
13150   int last_jx = player->last_jx, last_jy = player->last_jy;
13151   int move_stepsize = TILEX / player->move_delay_value;
13152
13153   if (!player->active)
13154     return;
13155
13156   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13157     return;
13158
13159   if (mode == SCROLL_INIT)
13160   {
13161     player->actual_frame_counter.count = FrameCounter;
13162     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13163
13164     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13165         Tile[last_jx][last_jy] == EL_EMPTY)
13166     {
13167       int last_field_block_delay = 0;   // start with no blocking at all
13168       int block_delay_adjustment = player->block_delay_adjustment;
13169
13170       // if player blocks last field, add delay for exactly one move
13171       if (player->block_last_field)
13172       {
13173         last_field_block_delay += player->move_delay_value;
13174
13175         // when blocking enabled, prevent moving up despite gravity
13176         if (player->gravity && player->MovDir == MV_UP)
13177           block_delay_adjustment = -1;
13178       }
13179
13180       // add block delay adjustment (also possible when not blocking)
13181       last_field_block_delay += block_delay_adjustment;
13182
13183       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13184       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13185     }
13186
13187     if (player->MovPos != 0)    // player has not yet reached destination
13188       return;
13189   }
13190   else if (!FrameReached(&player->actual_frame_counter))
13191     return;
13192
13193   if (player->MovPos != 0)
13194   {
13195     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13196     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13197
13198     // before DrawPlayer() to draw correct player graphic for this case
13199     if (player->MovPos == 0)
13200       CheckGravityMovement(player);
13201   }
13202
13203   if (player->MovPos == 0)      // player reached destination field
13204   {
13205     if (player->move_delay_reset_counter > 0)
13206     {
13207       player->move_delay_reset_counter--;
13208
13209       if (player->move_delay_reset_counter == 0)
13210       {
13211         // continue with normal speed after quickly moving through gate
13212         HALVE_PLAYER_SPEED(player);
13213
13214         // be able to make the next move without delay
13215         player->move_delay = 0;
13216       }
13217     }
13218
13219     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13220         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13221         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13222         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13223         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13224         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13225         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13226         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13227     {
13228       ExitPlayer(player);
13229
13230       if (game.players_still_needed == 0 &&
13231           (game.friends_still_needed == 0 ||
13232            IS_SP_ELEMENT(Tile[jx][jy])))
13233         LevelSolved();
13234     }
13235
13236     player->last_jx = jx;
13237     player->last_jy = jy;
13238
13239     // this breaks one level: "machine", level 000
13240     {
13241       int move_direction = player->MovDir;
13242       int enter_side = MV_DIR_OPPOSITE(move_direction);
13243       int leave_side = move_direction;
13244       int old_jx = last_jx;
13245       int old_jy = last_jy;
13246       int old_element = Tile[old_jx][old_jy];
13247       int new_element = Tile[jx][jy];
13248
13249       if (IS_CUSTOM_ELEMENT(old_element))
13250         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13251                                    CE_LEFT_BY_PLAYER,
13252                                    player->index_bit, leave_side);
13253
13254       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13255                                           CE_PLAYER_LEAVES_X,
13256                                           player->index_bit, leave_side);
13257
13258       // needed because pushed element has not yet reached its destination,
13259       // so it would trigger a change event at its previous field location
13260       if (!player->is_pushing)
13261       {
13262         if (IS_CUSTOM_ELEMENT(new_element))
13263           CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13264                                      player->index_bit, enter_side);
13265
13266         CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13267                                             CE_PLAYER_ENTERS_X,
13268                                             player->index_bit, enter_side);
13269       }
13270
13271       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13272                                         CE_MOVE_OF_X, move_direction);
13273     }
13274
13275     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13276     {
13277       TestIfPlayerTouchesBadThing(jx, jy);
13278       TestIfPlayerTouchesCustomElement(jx, jy);
13279
13280       // needed because pushed element has not yet reached its destination,
13281       // so it would trigger a change event at its previous field location
13282       if (!player->is_pushing)
13283         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13284
13285       if (level.finish_dig_collect &&
13286           (player->is_digging || player->is_collecting))
13287       {
13288         int last_element = player->last_removed_element;
13289         int move_direction = player->MovDir;
13290         int enter_side = MV_DIR_OPPOSITE(move_direction);
13291         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13292                             CE_PLAYER_COLLECTS_X);
13293
13294         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13295                                             player->index_bit, enter_side);
13296
13297         player->last_removed_element = EL_UNDEFINED;
13298       }
13299
13300       if (!player->active)
13301         RemovePlayer(player);
13302     }
13303
13304     if (level.use_step_counter)
13305       CheckLevelTime_StepCounter();
13306
13307     if (tape.single_step && tape.recording && !tape.pausing &&
13308         !player->programmed_action)
13309       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13310
13311     if (!player->programmed_action)
13312       CheckSaveEngineSnapshot(player);
13313   }
13314 }
13315
13316 void ScrollScreen(struct PlayerInfo *player, int mode)
13317 {
13318   static DelayCounter screen_frame_counter = { 0 };
13319
13320   if (mode == SCROLL_INIT)
13321   {
13322     // set scrolling step size according to actual player's moving speed
13323     ScrollStepSize = TILEX / player->move_delay_value;
13324
13325     screen_frame_counter.count = FrameCounter;
13326     screen_frame_counter.value = 1;
13327
13328     ScreenMovDir = player->MovDir;
13329     ScreenMovPos = player->MovPos;
13330     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13331     return;
13332   }
13333   else if (!FrameReached(&screen_frame_counter))
13334     return;
13335
13336   if (ScreenMovPos)
13337   {
13338     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13339     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13340     redraw_mask |= REDRAW_FIELD;
13341   }
13342   else
13343     ScreenMovDir = MV_NONE;
13344 }
13345
13346 void CheckNextToConditions(int x, int y)
13347 {
13348   int element = Tile[x][y];
13349
13350   if (IS_PLAYER(x, y))
13351     TestIfPlayerNextToCustomElement(x, y);
13352
13353   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13354       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13355     TestIfElementNextToCustomElement(x, y);
13356 }
13357
13358 void TestIfPlayerNextToCustomElement(int x, int y)
13359 {
13360   struct XY *xy = xy_topdown;
13361   static int trigger_sides[4][2] =
13362   {
13363     // center side       border side
13364     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13365     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13366     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13367     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13368   };
13369   int i;
13370
13371   if (!IS_PLAYER(x, y))
13372     return;
13373
13374   struct PlayerInfo *player = PLAYERINFO(x, y);
13375
13376   if (player->is_moving)
13377     return;
13378
13379   for (i = 0; i < NUM_DIRECTIONS; i++)
13380   {
13381     int xx = x + xy[i].x;
13382     int yy = y + xy[i].y;
13383     int border_side = trigger_sides[i][1];
13384     int border_element;
13385
13386     if (!IN_LEV_FIELD(xx, yy))
13387       continue;
13388
13389     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13390       continue;         // center and border element not connected
13391
13392     border_element = Tile[xx][yy];
13393
13394     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13395                                player->index_bit, border_side);
13396     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13397                                         CE_PLAYER_NEXT_TO_X,
13398                                         player->index_bit, border_side);
13399
13400     /* use player element that is initially defined in the level playfield,
13401        not the player element that corresponds to the runtime player number
13402        (example: a level that contains EL_PLAYER_3 as the only player would
13403        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13404
13405     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13406                              CE_NEXT_TO_X, border_side);
13407   }
13408 }
13409
13410 void TestIfPlayerTouchesCustomElement(int x, int y)
13411 {
13412   struct XY *xy = xy_topdown;
13413   static int trigger_sides[4][2] =
13414   {
13415     // center side       border side
13416     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13417     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13418     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13419     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13420   };
13421   static int touch_dir[4] =
13422   {
13423     MV_LEFT | MV_RIGHT,
13424     MV_UP   | MV_DOWN,
13425     MV_UP   | MV_DOWN,
13426     MV_LEFT | MV_RIGHT
13427   };
13428   int center_element = Tile[x][y];      // should always be non-moving!
13429   int i;
13430
13431   for (i = 0; i < NUM_DIRECTIONS; i++)
13432   {
13433     int xx = x + xy[i].x;
13434     int yy = y + xy[i].y;
13435     int center_side = trigger_sides[i][0];
13436     int border_side = trigger_sides[i][1];
13437     int border_element;
13438
13439     if (!IN_LEV_FIELD(xx, yy))
13440       continue;
13441
13442     if (IS_PLAYER(x, y))                // player found at center element
13443     {
13444       struct PlayerInfo *player = PLAYERINFO(x, y);
13445
13446       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13447         border_element = Tile[xx][yy];          // may be moving!
13448       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13449         border_element = Tile[xx][yy];
13450       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13451         border_element = MovingOrBlocked2Element(xx, yy);
13452       else
13453         continue;               // center and border element do not touch
13454
13455       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13456                                  player->index_bit, border_side);
13457       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13458                                           CE_PLAYER_TOUCHES_X,
13459                                           player->index_bit, border_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(x, y)->initial_element;
13467
13468         // as element "X" is the player here, check opposite (center) side
13469         CheckElementChangeBySide(xx, yy, border_element, player_element,
13470                                  CE_TOUCHING_X, center_side);
13471       }
13472     }
13473     else if (IS_PLAYER(xx, yy))         // player found at border element
13474     {
13475       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13476
13477       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13478       {
13479         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13480           continue;             // center and border element do not touch
13481       }
13482
13483       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13484                                  player->index_bit, center_side);
13485       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13486                                           CE_PLAYER_TOUCHES_X,
13487                                           player->index_bit, center_side);
13488
13489       {
13490         /* use player element that is initially defined in the level playfield,
13491            not the player element that corresponds to the runtime player number
13492            (example: a level that contains EL_PLAYER_3 as the only player would
13493            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13494         int player_element = PLAYERINFO(xx, yy)->initial_element;
13495
13496         // as element "X" is the player here, check opposite (border) side
13497         CheckElementChangeBySide(x, y, center_element, player_element,
13498                                  CE_TOUCHING_X, border_side);
13499       }
13500
13501       break;
13502     }
13503   }
13504 }
13505
13506 void TestIfElementNextToCustomElement(int x, int y)
13507 {
13508   struct XY *xy = xy_topdown;
13509   static int trigger_sides[4][2] =
13510   {
13511     // center side      border side
13512     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13513     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13514     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13515     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13516   };
13517   int center_element = Tile[x][y];      // should always be non-moving!
13518   int i;
13519
13520   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13521     return;
13522
13523   for (i = 0; i < NUM_DIRECTIONS; i++)
13524   {
13525     int xx = x + xy[i].x;
13526     int yy = y + xy[i].y;
13527     int border_side = trigger_sides[i][1];
13528     int border_element;
13529
13530     if (!IN_LEV_FIELD(xx, yy))
13531       continue;
13532
13533     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13534       continue;                 // center and border element not connected
13535
13536     border_element = Tile[xx][yy];
13537
13538     // check for change of center element (but change it only once)
13539     if (CheckElementChangeBySide(x, y, center_element, border_element,
13540                                  CE_NEXT_TO_X, border_side))
13541       break;
13542   }
13543 }
13544
13545 void TestIfElementTouchesCustomElement(int x, int y)
13546 {
13547   struct XY *xy = xy_topdown;
13548   static int trigger_sides[4][2] =
13549   {
13550     // center side      border side
13551     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13552     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13553     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13554     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13555   };
13556   static int touch_dir[4] =
13557   {
13558     MV_LEFT | MV_RIGHT,
13559     MV_UP   | MV_DOWN,
13560     MV_UP   | MV_DOWN,
13561     MV_LEFT | MV_RIGHT
13562   };
13563   boolean change_center_element = FALSE;
13564   int center_element = Tile[x][y];      // should always be non-moving!
13565   int border_element_old[NUM_DIRECTIONS];
13566   int i;
13567
13568   for (i = 0; i < NUM_DIRECTIONS; i++)
13569   {
13570     int xx = x + xy[i].x;
13571     int yy = y + xy[i].y;
13572     int border_element;
13573
13574     border_element_old[i] = -1;
13575
13576     if (!IN_LEV_FIELD(xx, yy))
13577       continue;
13578
13579     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13580       border_element = Tile[xx][yy];    // may be moving!
13581     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13582       border_element = Tile[xx][yy];
13583     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13584       border_element = MovingOrBlocked2Element(xx, yy);
13585     else
13586       continue;                 // center and border element do not touch
13587
13588     border_element_old[i] = border_element;
13589   }
13590
13591   for (i = 0; i < NUM_DIRECTIONS; i++)
13592   {
13593     int xx = x + xy[i].x;
13594     int yy = y + xy[i].y;
13595     int center_side = trigger_sides[i][0];
13596     int border_element = border_element_old[i];
13597
13598     if (border_element == -1)
13599       continue;
13600
13601     // check for change of border element
13602     CheckElementChangeBySide(xx, yy, border_element, center_element,
13603                              CE_TOUCHING_X, center_side);
13604
13605     // (center element cannot be player, so we don't have to check this here)
13606   }
13607
13608   for (i = 0; i < NUM_DIRECTIONS; i++)
13609   {
13610     int xx = x + xy[i].x;
13611     int yy = y + xy[i].y;
13612     int border_side = trigger_sides[i][1];
13613     int border_element = border_element_old[i];
13614
13615     if (border_element == -1)
13616       continue;
13617
13618     // check for change of center element (but change it only once)
13619     if (!change_center_element)
13620       change_center_element =
13621         CheckElementChangeBySide(x, y, center_element, border_element,
13622                                  CE_TOUCHING_X, border_side);
13623
13624     if (IS_PLAYER(xx, yy))
13625     {
13626       /* use player element that is initially defined in the level playfield,
13627          not the player element that corresponds to the runtime player number
13628          (example: a level that contains EL_PLAYER_3 as the only player would
13629          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13630       int player_element = PLAYERINFO(xx, yy)->initial_element;
13631
13632       // as element "X" is the player here, check opposite (border) side
13633       CheckElementChangeBySide(x, y, center_element, player_element,
13634                                CE_TOUCHING_X, border_side);
13635     }
13636   }
13637 }
13638
13639 void TestIfElementHitsCustomElement(int x, int y, int direction)
13640 {
13641   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13642   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13643   int hitx = x + dx, hity = y + dy;
13644   int hitting_element = Tile[x][y];
13645   int touched_element;
13646
13647   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13648     return;
13649
13650   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13651                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13652
13653   if (IN_LEV_FIELD(hitx, hity))
13654   {
13655     int opposite_direction = MV_DIR_OPPOSITE(direction);
13656     int hitting_side = direction;
13657     int touched_side = opposite_direction;
13658     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13659                           MovDir[hitx][hity] != direction ||
13660                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13661
13662     object_hit = TRUE;
13663
13664     if (object_hit)
13665     {
13666       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13667                                CE_HITTING_X, touched_side);
13668
13669       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13670                                CE_HIT_BY_X, hitting_side);
13671
13672       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13673                                CE_HIT_BY_SOMETHING, opposite_direction);
13674
13675       if (IS_PLAYER(hitx, hity))
13676       {
13677         /* use player element that is initially defined in the level playfield,
13678            not the player element that corresponds to the runtime player number
13679            (example: a level that contains EL_PLAYER_3 as the only player would
13680            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13681         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13682
13683         CheckElementChangeBySide(x, y, hitting_element, player_element,
13684                                  CE_HITTING_X, touched_side);
13685       }
13686     }
13687   }
13688
13689   // "hitting something" is also true when hitting the playfield border
13690   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13691                            CE_HITTING_SOMETHING, direction);
13692 }
13693
13694 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13695 {
13696   int i, kill_x = -1, kill_y = -1;
13697
13698   int bad_element = -1;
13699   struct XY *test_xy = xy_topdown;
13700   static int test_dir[4] =
13701   {
13702     MV_UP,
13703     MV_LEFT,
13704     MV_RIGHT,
13705     MV_DOWN
13706   };
13707
13708   for (i = 0; i < NUM_DIRECTIONS; i++)
13709   {
13710     int test_x, test_y, test_move_dir, test_element;
13711
13712     test_x = good_x + test_xy[i].x;
13713     test_y = good_y + test_xy[i].y;
13714
13715     if (!IN_LEV_FIELD(test_x, test_y))
13716       continue;
13717
13718     test_move_dir =
13719       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13720
13721     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13722
13723     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13724        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13725     */
13726     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13727         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13728     {
13729       kill_x = test_x;
13730       kill_y = test_y;
13731       bad_element = test_element;
13732
13733       break;
13734     }
13735   }
13736
13737   if (kill_x != -1 || kill_y != -1)
13738   {
13739     if (IS_PLAYER(good_x, good_y))
13740     {
13741       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13742
13743       if (player->shield_deadly_time_left > 0 &&
13744           !IS_INDESTRUCTIBLE(bad_element))
13745         Bang(kill_x, kill_y);
13746       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13747         KillPlayer(player);
13748     }
13749     else
13750       Bang(good_x, good_y);
13751   }
13752 }
13753
13754 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13755 {
13756   int i, kill_x = -1, kill_y = -1;
13757   int bad_element = Tile[bad_x][bad_y];
13758   struct XY *test_xy = xy_topdown;
13759   static int touch_dir[4] =
13760   {
13761     MV_LEFT | MV_RIGHT,
13762     MV_UP   | MV_DOWN,
13763     MV_UP   | MV_DOWN,
13764     MV_LEFT | MV_RIGHT
13765   };
13766   static int test_dir[4] =
13767   {
13768     MV_UP,
13769     MV_LEFT,
13770     MV_RIGHT,
13771     MV_DOWN
13772   };
13773
13774   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13775     return;
13776
13777   for (i = 0; i < NUM_DIRECTIONS; i++)
13778   {
13779     int test_x, test_y, test_move_dir, test_element;
13780
13781     test_x = bad_x + test_xy[i].x;
13782     test_y = bad_y + test_xy[i].y;
13783
13784     if (!IN_LEV_FIELD(test_x, test_y))
13785       continue;
13786
13787     test_move_dir =
13788       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13789
13790     test_element = Tile[test_x][test_y];
13791
13792     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13793        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13794     */
13795     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13796         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13797     {
13798       // good thing is player or penguin that does not move away
13799       if (IS_PLAYER(test_x, test_y))
13800       {
13801         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13802
13803         if (bad_element == EL_ROBOT && player->is_moving)
13804           continue;     // robot does not kill player if he is moving
13805
13806         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13807         {
13808           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13809             continue;           // center and border element do not touch
13810         }
13811
13812         kill_x = test_x;
13813         kill_y = test_y;
13814
13815         break;
13816       }
13817       else if (test_element == EL_PENGUIN)
13818       {
13819         kill_x = test_x;
13820         kill_y = test_y;
13821
13822         break;
13823       }
13824     }
13825   }
13826
13827   if (kill_x != -1 || kill_y != -1)
13828   {
13829     if (IS_PLAYER(kill_x, kill_y))
13830     {
13831       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13832
13833       if (player->shield_deadly_time_left > 0 &&
13834           !IS_INDESTRUCTIBLE(bad_element))
13835         Bang(bad_x, bad_y);
13836       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13837         KillPlayer(player);
13838     }
13839     else
13840       Bang(kill_x, kill_y);
13841   }
13842 }
13843
13844 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13845 {
13846   int bad_element = Tile[bad_x][bad_y];
13847   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13848   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13849   int test_x = bad_x + dx, test_y = bad_y + dy;
13850   int test_move_dir, test_element;
13851   int kill_x = -1, kill_y = -1;
13852
13853   if (!IN_LEV_FIELD(test_x, test_y))
13854     return;
13855
13856   test_move_dir =
13857     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13858
13859   test_element = Tile[test_x][test_y];
13860
13861   if (test_move_dir != bad_move_dir)
13862   {
13863     // good thing can be player or penguin that does not move away
13864     if (IS_PLAYER(test_x, test_y))
13865     {
13866       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13867
13868       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13869          player as being hit when he is moving towards the bad thing, because
13870          the "get hit by" condition would be lost after the player stops) */
13871       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13872         return;         // player moves away from bad thing
13873
13874       kill_x = test_x;
13875       kill_y = test_y;
13876     }
13877     else if (test_element == EL_PENGUIN)
13878     {
13879       kill_x = test_x;
13880       kill_y = test_y;
13881     }
13882   }
13883
13884   if (kill_x != -1 || kill_y != -1)
13885   {
13886     if (IS_PLAYER(kill_x, kill_y))
13887     {
13888       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13889
13890       if (player->shield_deadly_time_left > 0 &&
13891           !IS_INDESTRUCTIBLE(bad_element))
13892         Bang(bad_x, bad_y);
13893       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13894         KillPlayer(player);
13895     }
13896     else
13897       Bang(kill_x, kill_y);
13898   }
13899 }
13900
13901 void TestIfPlayerTouchesBadThing(int x, int y)
13902 {
13903   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13904 }
13905
13906 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13907 {
13908   TestIfGoodThingHitsBadThing(x, y, move_dir);
13909 }
13910
13911 void TestIfBadThingTouchesPlayer(int x, int y)
13912 {
13913   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13914 }
13915
13916 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13917 {
13918   TestIfBadThingHitsGoodThing(x, y, move_dir);
13919 }
13920
13921 void TestIfFriendTouchesBadThing(int x, int y)
13922 {
13923   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13924 }
13925
13926 void TestIfBadThingTouchesFriend(int x, int y)
13927 {
13928   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13929 }
13930
13931 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13932 {
13933   int i, kill_x = bad_x, kill_y = bad_y;
13934   struct XY *xy = xy_topdown;
13935
13936   for (i = 0; i < NUM_DIRECTIONS; i++)
13937   {
13938     int x, y, element;
13939
13940     x = bad_x + xy[i].x;
13941     y = bad_y + xy[i].y;
13942     if (!IN_LEV_FIELD(x, y))
13943       continue;
13944
13945     element = Tile[x][y];
13946     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13947         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13948     {
13949       kill_x = x;
13950       kill_y = y;
13951       break;
13952     }
13953   }
13954
13955   if (kill_x != bad_x || kill_y != bad_y)
13956     Bang(bad_x, bad_y);
13957 }
13958
13959 void KillPlayer(struct PlayerInfo *player)
13960 {
13961   int jx = player->jx, jy = player->jy;
13962
13963   if (!player->active)
13964     return;
13965
13966 #if 0
13967   Debug("game:playing:KillPlayer",
13968         "0: killed == %d, active == %d, reanimated == %d",
13969         player->killed, player->active, player->reanimated);
13970 #endif
13971
13972   /* the following code was introduced to prevent an infinite loop when calling
13973      -> Bang()
13974      -> CheckTriggeredElementChangeExt()
13975      -> ExecuteCustomElementAction()
13976      -> KillPlayer()
13977      -> (infinitely repeating the above sequence of function calls)
13978      which occurs when killing the player while having a CE with the setting
13979      "kill player X when explosion of <player X>"; the solution using a new
13980      field "player->killed" was chosen for backwards compatibility, although
13981      clever use of the fields "player->active" etc. would probably also work */
13982 #if 1
13983   if (player->killed)
13984     return;
13985 #endif
13986
13987   player->killed = TRUE;
13988
13989   // remove accessible field at the player's position
13990   RemoveField(jx, jy);
13991
13992   // deactivate shield (else Bang()/Explode() would not work right)
13993   player->shield_normal_time_left = 0;
13994   player->shield_deadly_time_left = 0;
13995
13996 #if 0
13997   Debug("game:playing:KillPlayer",
13998         "1: killed == %d, active == %d, reanimated == %d",
13999         player->killed, player->active, player->reanimated);
14000 #endif
14001
14002   Bang(jx, jy);
14003
14004 #if 0
14005   Debug("game:playing:KillPlayer",
14006         "2: killed == %d, active == %d, reanimated == %d",
14007         player->killed, player->active, player->reanimated);
14008 #endif
14009
14010   if (player->reanimated)       // killed player may have been reanimated
14011     player->killed = player->reanimated = FALSE;
14012   else
14013     BuryPlayer(player);
14014 }
14015
14016 static void KillPlayerUnlessEnemyProtected(int x, int y)
14017 {
14018   if (!PLAYER_ENEMY_PROTECTED(x, y))
14019     KillPlayer(PLAYERINFO(x, y));
14020 }
14021
14022 static void KillPlayerUnlessExplosionProtected(int x, int y)
14023 {
14024   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14025     KillPlayer(PLAYERINFO(x, y));
14026 }
14027
14028 void BuryPlayer(struct PlayerInfo *player)
14029 {
14030   int jx = player->jx, jy = player->jy;
14031
14032   if (!player->active)
14033     return;
14034
14035   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14036
14037   RemovePlayer(player);
14038
14039   player->buried = TRUE;
14040
14041   if (game.all_players_gone)
14042     game.GameOver = TRUE;
14043 }
14044
14045 void RemovePlayer(struct PlayerInfo *player)
14046 {
14047   int jx = player->jx, jy = player->jy;
14048   int i, found = FALSE;
14049
14050   player->present = FALSE;
14051   player->active = FALSE;
14052
14053   // required for some CE actions (even if the player is not active anymore)
14054   player->MovPos = 0;
14055
14056   if (!ExplodeField[jx][jy])
14057     StorePlayer[jx][jy] = 0;
14058
14059   if (player->is_moving)
14060     TEST_DrawLevelField(player->last_jx, player->last_jy);
14061
14062   for (i = 0; i < MAX_PLAYERS; i++)
14063     if (stored_player[i].active)
14064       found = TRUE;
14065
14066   if (!found)
14067   {
14068     game.all_players_gone = TRUE;
14069     game.GameOver = TRUE;
14070   }
14071
14072   game.exit_x = game.robot_wheel_x = jx;
14073   game.exit_y = game.robot_wheel_y = jy;
14074 }
14075
14076 void ExitPlayer(struct PlayerInfo *player)
14077 {
14078   DrawPlayer(player);   // needed here only to cleanup last field
14079   RemovePlayer(player);
14080
14081   if (game.players_still_needed > 0)
14082     game.players_still_needed--;
14083 }
14084
14085 static void SetFieldForSnapping(int x, int y, int element, int direction,
14086                                 int player_index_bit)
14087 {
14088   struct ElementInfo *ei = &element_info[element];
14089   int direction_bit = MV_DIR_TO_BIT(direction);
14090   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14091   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14092                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14093
14094   Tile[x][y] = EL_ELEMENT_SNAPPING;
14095   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14096   MovDir[x][y] = direction;
14097   Store[x][y] = element;
14098   Store2[x][y] = player_index_bit;
14099
14100   ResetGfxAnimation(x, y);
14101
14102   GfxElement[x][y] = element;
14103   GfxAction[x][y] = action;
14104   GfxDir[x][y] = direction;
14105   GfxFrame[x][y] = -1;
14106 }
14107
14108 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14109                                    int player_index_bit)
14110 {
14111   TestIfElementTouchesCustomElement(x, y);      // for empty space
14112
14113   if (level.finish_dig_collect)
14114   {
14115     int dig_side = MV_DIR_OPPOSITE(direction);
14116     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14117                         CE_PLAYER_COLLECTS_X);
14118
14119     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14120                                         player_index_bit, dig_side);
14121     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14122                                         player_index_bit, dig_side);
14123   }
14124 }
14125
14126 /*
14127   =============================================================================
14128   checkDiagonalPushing()
14129   -----------------------------------------------------------------------------
14130   check if diagonal input device direction results in pushing of object
14131   (by checking if the alternative direction is walkable, diggable, ...)
14132   =============================================================================
14133 */
14134
14135 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14136                                     int x, int y, int real_dx, int real_dy)
14137 {
14138   int jx, jy, dx, dy, xx, yy;
14139
14140   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14141     return TRUE;
14142
14143   // diagonal direction: check alternative direction
14144   jx = player->jx;
14145   jy = player->jy;
14146   dx = x - jx;
14147   dy = y - jy;
14148   xx = jx + (dx == 0 ? real_dx : 0);
14149   yy = jy + (dy == 0 ? real_dy : 0);
14150
14151   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14152 }
14153
14154 /*
14155   =============================================================================
14156   DigField()
14157   -----------------------------------------------------------------------------
14158   x, y:                 field next to player (non-diagonal) to try to dig to
14159   real_dx, real_dy:     direction as read from input device (can be diagonal)
14160   =============================================================================
14161 */
14162
14163 static int DigField(struct PlayerInfo *player,
14164                     int oldx, int oldy, int x, int y,
14165                     int real_dx, int real_dy, int mode)
14166 {
14167   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14168   boolean player_was_pushing = player->is_pushing;
14169   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14170   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14171   int jx = oldx, jy = oldy;
14172   int dx = x - jx, dy = y - jy;
14173   int nextx = x + dx, nexty = y + dy;
14174   int move_direction = (dx == -1 ? MV_LEFT  :
14175                         dx == +1 ? MV_RIGHT :
14176                         dy == -1 ? MV_UP    :
14177                         dy == +1 ? MV_DOWN  : MV_NONE);
14178   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14179   int dig_side = MV_DIR_OPPOSITE(move_direction);
14180   int old_element = Tile[jx][jy];
14181   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14182   int collect_count;
14183
14184   if (is_player)                // function can also be called by EL_PENGUIN
14185   {
14186     if (player->MovPos == 0)
14187     {
14188       player->is_digging = FALSE;
14189       player->is_collecting = FALSE;
14190     }
14191
14192     if (player->MovPos == 0)    // last pushing move finished
14193       player->is_pushing = FALSE;
14194
14195     if (mode == DF_NO_PUSH)     // player just stopped pushing
14196     {
14197       player->is_switching = FALSE;
14198       player->push_delay = -1;
14199
14200       return MP_NO_ACTION;
14201     }
14202   }
14203   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14204     old_element = Back[jx][jy];
14205
14206   // in case of element dropped at player position, check background
14207   else if (Back[jx][jy] != EL_EMPTY &&
14208            game.engine_version >= VERSION_IDENT(2,2,0,0))
14209     old_element = Back[jx][jy];
14210
14211   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14212     return MP_NO_ACTION;        // field has no opening in this direction
14213
14214   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element, opposite_direction))
14215     return MP_NO_ACTION;        // field has no opening in this direction
14216
14217   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14218   {
14219     SplashAcid(x, y);
14220
14221     Tile[jx][jy] = player->artwork_element;
14222     InitMovingField(jx, jy, MV_DOWN);
14223     Store[jx][jy] = EL_ACID;
14224     ContinueMoving(jx, jy);
14225     BuryPlayer(player);
14226
14227     return MP_DONT_RUN_INTO;
14228   }
14229
14230   if (player_can_move && DONT_RUN_INTO(element))
14231   {
14232     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14233
14234     return MP_DONT_RUN_INTO;
14235   }
14236
14237   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14238     return MP_NO_ACTION;
14239
14240   collect_count = element_info[element].collect_count_initial;
14241
14242   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14243     return MP_NO_ACTION;
14244
14245   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14246     player_can_move = player_can_move_or_snap;
14247
14248   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14249       game.engine_version >= VERSION_IDENT(2,2,0,0))
14250   {
14251     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14252                                player->index_bit, dig_side);
14253     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14254                                         player->index_bit, dig_side);
14255
14256     if (element == EL_DC_LANDMINE)
14257       Bang(x, y);
14258
14259     if (Tile[x][y] != element)          // field changed by snapping
14260       return MP_ACTION;
14261
14262     return MP_NO_ACTION;
14263   }
14264
14265   if (player->gravity && is_player && !player->is_auto_moving &&
14266       canFallDown(player) && move_direction != MV_DOWN &&
14267       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14268     return MP_NO_ACTION;        // player cannot walk here due to gravity
14269
14270   if (player_can_move &&
14271       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14272   {
14273     int sound_element = SND_ELEMENT(element);
14274     int sound_action = ACTION_WALKING;
14275
14276     if (IS_RND_GATE(element))
14277     {
14278       if (!player->key[RND_GATE_NR(element)])
14279         return MP_NO_ACTION;
14280     }
14281     else if (IS_RND_GATE_GRAY(element))
14282     {
14283       if (!player->key[RND_GATE_GRAY_NR(element)])
14284         return MP_NO_ACTION;
14285     }
14286     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14287     {
14288       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14289         return MP_NO_ACTION;
14290     }
14291     else if (element == EL_EXIT_OPEN ||
14292              element == EL_EM_EXIT_OPEN ||
14293              element == EL_EM_EXIT_OPENING ||
14294              element == EL_STEEL_EXIT_OPEN ||
14295              element == EL_EM_STEEL_EXIT_OPEN ||
14296              element == EL_EM_STEEL_EXIT_OPENING ||
14297              element == EL_SP_EXIT_OPEN ||
14298              element == EL_SP_EXIT_OPENING)
14299     {
14300       sound_action = ACTION_PASSING;    // player is passing exit
14301     }
14302     else if (element == EL_EMPTY)
14303     {
14304       sound_action = ACTION_MOVING;             // nothing to walk on
14305     }
14306
14307     // play sound from background or player, whatever is available
14308     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14309       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14310     else
14311       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14312   }
14313   else if (player_can_move &&
14314            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14315   {
14316     if (!ACCESS_FROM(element, opposite_direction))
14317       return MP_NO_ACTION;      // field not accessible from this direction
14318
14319     if (CAN_MOVE(element))      // only fixed elements can be passed!
14320       return MP_NO_ACTION;
14321
14322     if (IS_EM_GATE(element))
14323     {
14324       if (!player->key[EM_GATE_NR(element)])
14325         return MP_NO_ACTION;
14326     }
14327     else if (IS_EM_GATE_GRAY(element))
14328     {
14329       if (!player->key[EM_GATE_GRAY_NR(element)])
14330         return MP_NO_ACTION;
14331     }
14332     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14333     {
14334       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14335         return MP_NO_ACTION;
14336     }
14337     else if (IS_EMC_GATE(element))
14338     {
14339       if (!player->key[EMC_GATE_NR(element)])
14340         return MP_NO_ACTION;
14341     }
14342     else if (IS_EMC_GATE_GRAY(element))
14343     {
14344       if (!player->key[EMC_GATE_GRAY_NR(element)])
14345         return MP_NO_ACTION;
14346     }
14347     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14348     {
14349       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14350         return MP_NO_ACTION;
14351     }
14352     else if (element == EL_DC_GATE_WHITE ||
14353              element == EL_DC_GATE_WHITE_GRAY ||
14354              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14355     {
14356       if (player->num_white_keys == 0)
14357         return MP_NO_ACTION;
14358
14359       player->num_white_keys--;
14360     }
14361     else if (IS_SP_PORT(element))
14362     {
14363       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14364           element == EL_SP_GRAVITY_PORT_RIGHT ||
14365           element == EL_SP_GRAVITY_PORT_UP ||
14366           element == EL_SP_GRAVITY_PORT_DOWN)
14367         player->gravity = !player->gravity;
14368       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14369                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14370                element == EL_SP_GRAVITY_ON_PORT_UP ||
14371                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14372         player->gravity = TRUE;
14373       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14374                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14375                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14376                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14377         player->gravity = FALSE;
14378     }
14379
14380     // automatically move to the next field with double speed
14381     player->programmed_action = move_direction;
14382
14383     if (player->move_delay_reset_counter == 0)
14384     {
14385       player->move_delay_reset_counter = 2;     // two double speed steps
14386
14387       DOUBLE_PLAYER_SPEED(player);
14388     }
14389
14390     PlayLevelSoundAction(x, y, ACTION_PASSING);
14391   }
14392   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14393   {
14394     RemoveField(x, y);
14395
14396     if (mode != DF_SNAP)
14397     {
14398       GfxElement[x][y] = GFX_ELEMENT(element);
14399       player->is_digging = TRUE;
14400     }
14401
14402     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14403
14404     // use old behaviour for old levels (digging)
14405     if (!level.finish_dig_collect)
14406     {
14407       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14408                                           player->index_bit, dig_side);
14409
14410       // if digging triggered player relocation, finish digging tile
14411       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14412         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14413     }
14414
14415     if (mode == DF_SNAP)
14416     {
14417       if (level.block_snap_field)
14418         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14419       else
14420         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14421
14422       // use old behaviour for old levels (snapping)
14423       if (!level.finish_dig_collect)
14424         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14425                                             player->index_bit, dig_side);
14426     }
14427   }
14428   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14429   {
14430     RemoveField(x, y);
14431
14432     if (is_player && mode != DF_SNAP)
14433     {
14434       GfxElement[x][y] = element;
14435       player->is_collecting = TRUE;
14436     }
14437
14438     if (element == EL_SPEED_PILL)
14439     {
14440       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14441     }
14442     else if (element == EL_EXTRA_TIME && level.time > 0)
14443     {
14444       TimeLeft += level.extra_time;
14445
14446       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14447
14448       DisplayGameControlValues();
14449     }
14450     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14451     {
14452       int shield_time = (element == EL_SHIELD_DEADLY ?
14453                          level.shield_deadly_time :
14454                          level.shield_normal_time);
14455
14456       player->shield_normal_time_left += shield_time;
14457       if (element == EL_SHIELD_DEADLY)
14458         player->shield_deadly_time_left += shield_time;
14459     }
14460     else if (element == EL_DYNAMITE ||
14461              element == EL_EM_DYNAMITE ||
14462              element == EL_SP_DISK_RED)
14463     {
14464       if (player->inventory_size < MAX_INVENTORY_SIZE)
14465         player->inventory_element[player->inventory_size++] = element;
14466
14467       DrawGameDoorValues();
14468     }
14469     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14470     {
14471       player->dynabomb_count++;
14472       player->dynabombs_left++;
14473     }
14474     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14475     {
14476       player->dynabomb_size++;
14477     }
14478     else if (element == EL_DYNABOMB_INCREASE_POWER)
14479     {
14480       player->dynabomb_xl = TRUE;
14481     }
14482     else if (IS_KEY(element))
14483     {
14484       player->key[KEY_NR(element)] = TRUE;
14485
14486       DrawGameDoorValues();
14487     }
14488     else if (element == EL_DC_KEY_WHITE)
14489     {
14490       player->num_white_keys++;
14491
14492       // display white keys?
14493       // DrawGameDoorValues();
14494     }
14495     else if (IS_ENVELOPE(element))
14496     {
14497       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14498
14499       if (!wait_for_snapping)
14500         player->show_envelope = element;
14501     }
14502     else if (element == EL_EMC_LENSES)
14503     {
14504       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14505
14506       RedrawAllInvisibleElementsForLenses();
14507     }
14508     else if (element == EL_EMC_MAGNIFIER)
14509     {
14510       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14511
14512       RedrawAllInvisibleElementsForMagnifier();
14513     }
14514     else if (IS_DROPPABLE(element) ||
14515              IS_THROWABLE(element))     // can be collected and dropped
14516     {
14517       int i;
14518
14519       if (collect_count == 0)
14520         player->inventory_infinite_element = element;
14521       else
14522         for (i = 0; i < collect_count; i++)
14523           if (player->inventory_size < MAX_INVENTORY_SIZE)
14524             player->inventory_element[player->inventory_size++] = element;
14525
14526       DrawGameDoorValues();
14527     }
14528     else if (collect_count > 0)
14529     {
14530       game.gems_still_needed -= collect_count;
14531       if (game.gems_still_needed < 0)
14532         game.gems_still_needed = 0;
14533
14534       game.snapshot.collected_item = TRUE;
14535
14536       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14537
14538       DisplayGameControlValues();
14539     }
14540
14541     RaiseScoreElement(element);
14542     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14543
14544     // use old behaviour for old levels (collecting)
14545     if (!level.finish_dig_collect && is_player)
14546     {
14547       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14548                                           player->index_bit, dig_side);
14549
14550       // if collecting triggered player relocation, finish collecting tile
14551       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14552         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14553     }
14554
14555     if (mode == DF_SNAP)
14556     {
14557       if (level.block_snap_field)
14558         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14559       else
14560         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14561
14562       // use old behaviour for old levels (snapping)
14563       if (!level.finish_dig_collect)
14564         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14565                                             player->index_bit, dig_side);
14566     }
14567   }
14568   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14569   {
14570     if (mode == DF_SNAP && element != EL_BD_ROCK)
14571       return MP_NO_ACTION;
14572
14573     if (CAN_FALL(element) && dy)
14574       return MP_NO_ACTION;
14575
14576     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14577         !(element == EL_SPRING && level.use_spring_bug))
14578       return MP_NO_ACTION;
14579
14580     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14581         ((move_direction & MV_VERTICAL &&
14582           ((element_info[element].move_pattern & MV_LEFT &&
14583             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14584            (element_info[element].move_pattern & MV_RIGHT &&
14585             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14586          (move_direction & MV_HORIZONTAL &&
14587           ((element_info[element].move_pattern & MV_UP &&
14588             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14589            (element_info[element].move_pattern & MV_DOWN &&
14590             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14591       return MP_NO_ACTION;
14592
14593     // do not push elements already moving away faster than player
14594     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14595         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14596       return MP_NO_ACTION;
14597
14598     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14599     {
14600       if (player->push_delay_value == -1 || !player_was_pushing)
14601         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14602     }
14603     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14604     {
14605       if (player->push_delay_value == -1)
14606         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14607     }
14608     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14609     {
14610       if (!player->is_pushing)
14611         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14612     }
14613
14614     player->is_pushing = TRUE;
14615     player->is_active = TRUE;
14616
14617     if (!(IN_LEV_FIELD(nextx, nexty) &&
14618           (IS_FREE(nextx, nexty) ||
14619            (IS_SB_ELEMENT(element) &&
14620             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14621            (IS_CUSTOM_ELEMENT(element) &&
14622             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14623       return MP_NO_ACTION;
14624
14625     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14626       return MP_NO_ACTION;
14627
14628     if (player->push_delay == -1)       // new pushing; restart delay
14629       player->push_delay = 0;
14630
14631     if (player->push_delay < player->push_delay_value &&
14632         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14633         element != EL_SPRING && element != EL_BALLOON)
14634     {
14635       // make sure that there is no move delay before next try to push
14636       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14637         player->move_delay = 0;
14638
14639       return MP_NO_ACTION;
14640     }
14641
14642     if (IS_CUSTOM_ELEMENT(element) &&
14643         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14644     {
14645       if (!DigFieldByCE(nextx, nexty, element))
14646         return MP_NO_ACTION;
14647     }
14648
14649     if (IS_SB_ELEMENT(element))
14650     {
14651       boolean sokoban_task_solved = FALSE;
14652
14653       if (element == EL_SOKOBAN_FIELD_FULL)
14654       {
14655         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14656
14657         IncrementSokobanFieldsNeeded();
14658         IncrementSokobanObjectsNeeded();
14659       }
14660
14661       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14662       {
14663         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14664
14665         DecrementSokobanFieldsNeeded();
14666         DecrementSokobanObjectsNeeded();
14667
14668         // sokoban object was pushed from empty field to sokoban field
14669         if (Back[x][y] == EL_EMPTY)
14670           sokoban_task_solved = TRUE;
14671       }
14672
14673       Tile[x][y] = EL_SOKOBAN_OBJECT;
14674
14675       if (Back[x][y] == Back[nextx][nexty])
14676         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14677       else if (Back[x][y] != 0)
14678         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14679                                     ACTION_EMPTYING);
14680       else
14681         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14682                                     ACTION_FILLING);
14683
14684       if (sokoban_task_solved &&
14685           game.sokoban_fields_still_needed == 0 &&
14686           game.sokoban_objects_still_needed == 0 &&
14687           level.auto_exit_sokoban)
14688       {
14689         game.players_still_needed = 0;
14690
14691         LevelSolved();
14692
14693         PlaySound(SND_GAME_SOKOBAN_SOLVING);
14694       }
14695     }
14696     else
14697       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14698
14699     InitMovingField(x, y, move_direction);
14700     GfxAction[x][y] = ACTION_PUSHING;
14701
14702     if (mode == DF_SNAP)
14703       ContinueMoving(x, y);
14704     else
14705       MovPos[x][y] = (dx != 0 ? dx : dy);
14706
14707     Pushed[x][y] = TRUE;
14708     Pushed[nextx][nexty] = TRUE;
14709
14710     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14711       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14712     else
14713       player->push_delay_value = -1;    // get new value later
14714
14715     // check for element change _after_ element has been pushed
14716     if (game.use_change_when_pushing_bug)
14717     {
14718       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14719                                  player->index_bit, dig_side);
14720       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14721                                           player->index_bit, dig_side);
14722     }
14723   }
14724   else if (IS_SWITCHABLE(element))
14725   {
14726     if (PLAYER_SWITCHING(player, x, y))
14727     {
14728       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14729                                           player->index_bit, dig_side);
14730
14731       return MP_ACTION;
14732     }
14733
14734     player->is_switching = TRUE;
14735     player->switch_x = x;
14736     player->switch_y = y;
14737
14738     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14739
14740     if (element == EL_ROBOT_WHEEL)
14741     {
14742       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14743
14744       game.robot_wheel_x = x;
14745       game.robot_wheel_y = y;
14746       game.robot_wheel_active = TRUE;
14747
14748       TEST_DrawLevelField(x, y);
14749     }
14750     else if (element == EL_SP_TERMINAL)
14751     {
14752       int xx, yy;
14753
14754       SCAN_PLAYFIELD(xx, yy)
14755       {
14756         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14757         {
14758           Bang(xx, yy);
14759         }
14760         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14761         {
14762           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14763
14764           ResetGfxAnimation(xx, yy);
14765           TEST_DrawLevelField(xx, yy);
14766         }
14767       }
14768     }
14769     else if (IS_BELT_SWITCH(element))
14770     {
14771       ToggleBeltSwitch(x, y);
14772     }
14773     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14774              element == EL_SWITCHGATE_SWITCH_DOWN ||
14775              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14776              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14777     {
14778       ToggleSwitchgateSwitch();
14779     }
14780     else if (element == EL_LIGHT_SWITCH ||
14781              element == EL_LIGHT_SWITCH_ACTIVE)
14782     {
14783       ToggleLightSwitch(x, y);
14784     }
14785     else if (element == EL_TIMEGATE_SWITCH ||
14786              element == EL_DC_TIMEGATE_SWITCH)
14787     {
14788       ActivateTimegateSwitch(x, y);
14789     }
14790     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14791              element == EL_BALLOON_SWITCH_RIGHT ||
14792              element == EL_BALLOON_SWITCH_UP    ||
14793              element == EL_BALLOON_SWITCH_DOWN  ||
14794              element == EL_BALLOON_SWITCH_NONE  ||
14795              element == EL_BALLOON_SWITCH_ANY)
14796     {
14797       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14798                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14799                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14800                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14801                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14802                              move_direction);
14803     }
14804     else if (element == EL_LAMP)
14805     {
14806       Tile[x][y] = EL_LAMP_ACTIVE;
14807       game.lights_still_needed--;
14808
14809       ResetGfxAnimation(x, y);
14810       TEST_DrawLevelField(x, y);
14811     }
14812     else if (element == EL_TIME_ORB_FULL)
14813     {
14814       Tile[x][y] = EL_TIME_ORB_EMPTY;
14815
14816       if (level.time > 0 || level.use_time_orb_bug)
14817       {
14818         TimeLeft += level.time_orb_time;
14819         game.no_level_time_limit = FALSE;
14820
14821         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14822
14823         DisplayGameControlValues();
14824       }
14825
14826       ResetGfxAnimation(x, y);
14827       TEST_DrawLevelField(x, y);
14828     }
14829     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14830              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14831     {
14832       int xx, yy;
14833
14834       game.ball_active = !game.ball_active;
14835
14836       SCAN_PLAYFIELD(xx, yy)
14837       {
14838         int e = Tile[xx][yy];
14839
14840         if (game.ball_active)
14841         {
14842           if (e == EL_EMC_MAGIC_BALL)
14843             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14844           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14845             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14846         }
14847         else
14848         {
14849           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14850             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14851           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14852             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14853         }
14854       }
14855     }
14856
14857     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14858                                         player->index_bit, dig_side);
14859
14860     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14861                                         player->index_bit, dig_side);
14862
14863     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14864                                         player->index_bit, dig_side);
14865
14866     return MP_ACTION;
14867   }
14868   else
14869   {
14870     if (!PLAYER_SWITCHING(player, x, y))
14871     {
14872       player->is_switching = TRUE;
14873       player->switch_x = x;
14874       player->switch_y = y;
14875
14876       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14877                                  player->index_bit, dig_side);
14878       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14879                                           player->index_bit, dig_side);
14880
14881       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14882                                  player->index_bit, dig_side);
14883       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14884                                           player->index_bit, dig_side);
14885     }
14886
14887     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14888                                player->index_bit, dig_side);
14889     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14890                                         player->index_bit, dig_side);
14891
14892     return MP_NO_ACTION;
14893   }
14894
14895   player->push_delay = -1;
14896
14897   if (is_player)                // function can also be called by EL_PENGUIN
14898   {
14899     if (Tile[x][y] != element)          // really digged/collected something
14900     {
14901       player->is_collecting = !player->is_digging;
14902       player->is_active = TRUE;
14903
14904       player->last_removed_element = element;
14905     }
14906   }
14907
14908   return MP_MOVING;
14909 }
14910
14911 static boolean DigFieldByCE(int x, int y, int digging_element)
14912 {
14913   int element = Tile[x][y];
14914
14915   if (!IS_FREE(x, y))
14916   {
14917     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14918                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14919                   ACTION_BREAKING);
14920
14921     // no element can dig solid indestructible elements
14922     if (IS_INDESTRUCTIBLE(element) &&
14923         !IS_DIGGABLE(element) &&
14924         !IS_COLLECTIBLE(element))
14925       return FALSE;
14926
14927     if (AmoebaNr[x][y] &&
14928         (element == EL_AMOEBA_FULL ||
14929          element == EL_BD_AMOEBA ||
14930          element == EL_AMOEBA_GROWING))
14931     {
14932       AmoebaCnt[AmoebaNr[x][y]]--;
14933       AmoebaCnt2[AmoebaNr[x][y]]--;
14934     }
14935
14936     if (IS_MOVING(x, y))
14937       RemoveMovingField(x, y);
14938     else
14939     {
14940       RemoveField(x, y);
14941       TEST_DrawLevelField(x, y);
14942     }
14943
14944     // if digged element was about to explode, prevent the explosion
14945     ExplodeField[x][y] = EX_TYPE_NONE;
14946
14947     PlayLevelSoundAction(x, y, action);
14948   }
14949
14950   Store[x][y] = EL_EMPTY;
14951
14952   // this makes it possible to leave the removed element again
14953   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14954     Store[x][y] = element;
14955
14956   return TRUE;
14957 }
14958
14959 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14960 {
14961   int jx = player->jx, jy = player->jy;
14962   int x = jx + dx, y = jy + dy;
14963   int snap_direction = (dx == -1 ? MV_LEFT  :
14964                         dx == +1 ? MV_RIGHT :
14965                         dy == -1 ? MV_UP    :
14966                         dy == +1 ? MV_DOWN  : MV_NONE);
14967   boolean can_continue_snapping = (level.continuous_snapping &&
14968                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14969
14970   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14971     return FALSE;
14972
14973   if (!player->active || !IN_LEV_FIELD(x, y))
14974     return FALSE;
14975
14976   if (dx && dy)
14977     return FALSE;
14978
14979   if (!dx && !dy)
14980   {
14981     if (player->MovPos == 0)
14982       player->is_pushing = FALSE;
14983
14984     player->is_snapping = FALSE;
14985
14986     if (player->MovPos == 0)
14987     {
14988       player->is_moving = FALSE;
14989       player->is_digging = FALSE;
14990       player->is_collecting = FALSE;
14991     }
14992
14993     return FALSE;
14994   }
14995
14996   // prevent snapping with already pressed snap key when not allowed
14997   if (player->is_snapping && !can_continue_snapping)
14998     return FALSE;
14999
15000   player->MovDir = snap_direction;
15001
15002   if (player->MovPos == 0)
15003   {
15004     player->is_moving = FALSE;
15005     player->is_digging = FALSE;
15006     player->is_collecting = FALSE;
15007   }
15008
15009   player->is_dropping = FALSE;
15010   player->is_dropping_pressed = FALSE;
15011   player->drop_pressed_delay = 0;
15012
15013   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15014     return FALSE;
15015
15016   player->is_snapping = TRUE;
15017   player->is_active = TRUE;
15018
15019   if (player->MovPos == 0)
15020   {
15021     player->is_moving = FALSE;
15022     player->is_digging = FALSE;
15023     player->is_collecting = FALSE;
15024   }
15025
15026   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
15027     TEST_DrawLevelField(player->last_jx, player->last_jy);
15028
15029   TEST_DrawLevelField(x, y);
15030
15031   return TRUE;
15032 }
15033
15034 static boolean DropElement(struct PlayerInfo *player)
15035 {
15036   int old_element, new_element;
15037   int dropx = player->jx, dropy = player->jy;
15038   int drop_direction = player->MovDir;
15039   int drop_side = drop_direction;
15040   int drop_element = get_next_dropped_element(player);
15041
15042   /* do not drop an element on top of another element; when holding drop key
15043      pressed without moving, dropped element must move away before the next
15044      element can be dropped (this is especially important if the next element
15045      is dynamite, which can be placed on background for historical reasons) */
15046   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
15047     return MP_ACTION;
15048
15049   if (IS_THROWABLE(drop_element))
15050   {
15051     dropx += GET_DX_FROM_DIR(drop_direction);
15052     dropy += GET_DY_FROM_DIR(drop_direction);
15053
15054     if (!IN_LEV_FIELD(dropx, dropy))
15055       return FALSE;
15056   }
15057
15058   old_element = Tile[dropx][dropy];     // old element at dropping position
15059   new_element = drop_element;           // default: no change when dropping
15060
15061   // check if player is active, not moving and ready to drop
15062   if (!player->active || player->MovPos || player->drop_delay > 0)
15063     return FALSE;
15064
15065   // check if player has anything that can be dropped
15066   if (new_element == EL_UNDEFINED)
15067     return FALSE;
15068
15069   // only set if player has anything that can be dropped
15070   player->is_dropping_pressed = TRUE;
15071
15072   // check if drop key was pressed long enough for EM style dynamite
15073   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15074     return FALSE;
15075
15076   // check if anything can be dropped at the current position
15077   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15078     return FALSE;
15079
15080   // collected custom elements can only be dropped on empty fields
15081   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15082     return FALSE;
15083
15084   if (old_element != EL_EMPTY)
15085     Back[dropx][dropy] = old_element;   // store old element on this field
15086
15087   ResetGfxAnimation(dropx, dropy);
15088   ResetRandomAnimationValue(dropx, dropy);
15089
15090   if (player->inventory_size > 0 ||
15091       player->inventory_infinite_element != EL_UNDEFINED)
15092   {
15093     if (player->inventory_size > 0)
15094     {
15095       player->inventory_size--;
15096
15097       DrawGameDoorValues();
15098
15099       if (new_element == EL_DYNAMITE)
15100         new_element = EL_DYNAMITE_ACTIVE;
15101       else if (new_element == EL_EM_DYNAMITE)
15102         new_element = EL_EM_DYNAMITE_ACTIVE;
15103       else if (new_element == EL_SP_DISK_RED)
15104         new_element = EL_SP_DISK_RED_ACTIVE;
15105     }
15106
15107     Tile[dropx][dropy] = new_element;
15108
15109     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15110       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15111                           el2img(Tile[dropx][dropy]), 0);
15112
15113     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15114
15115     // needed if previous element just changed to "empty" in the last frame
15116     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15117
15118     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15119                                player->index_bit, drop_side);
15120     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15121                                         CE_PLAYER_DROPS_X,
15122                                         player->index_bit, drop_side);
15123
15124     TestIfElementTouchesCustomElement(dropx, dropy);
15125   }
15126   else          // player is dropping a dyna bomb
15127   {
15128     player->dynabombs_left--;
15129
15130     Tile[dropx][dropy] = new_element;
15131
15132     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15133       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15134                           el2img(Tile[dropx][dropy]), 0);
15135
15136     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15137   }
15138
15139   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15140     InitField_WithBug1(dropx, dropy, FALSE);
15141
15142   new_element = Tile[dropx][dropy];     // element might have changed
15143
15144   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15145       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15146   {
15147     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15148       MovDir[dropx][dropy] = drop_direction;
15149
15150     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15151
15152     // do not cause impact style collision by dropping elements that can fall
15153     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15154   }
15155
15156   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15157   player->is_dropping = TRUE;
15158
15159   player->drop_pressed_delay = 0;
15160   player->is_dropping_pressed = FALSE;
15161
15162   player->drop_x = dropx;
15163   player->drop_y = dropy;
15164
15165   return TRUE;
15166 }
15167
15168 // ----------------------------------------------------------------------------
15169 // game sound playing functions
15170 // ----------------------------------------------------------------------------
15171
15172 static int *loop_sound_frame = NULL;
15173 static int *loop_sound_volume = NULL;
15174
15175 void InitPlayLevelSound(void)
15176 {
15177   int num_sounds = getSoundListSize();
15178
15179   checked_free(loop_sound_frame);
15180   checked_free(loop_sound_volume);
15181
15182   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15183   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15184 }
15185
15186 static void PlayLevelSound(int x, int y, int nr)
15187 {
15188   int sx = SCREENX(x), sy = SCREENY(y);
15189   int volume, stereo_position;
15190   int max_distance = 8;
15191   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15192
15193   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15194       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15195     return;
15196
15197   if (!IN_LEV_FIELD(x, y) ||
15198       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15199       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15200     return;
15201
15202   volume = SOUND_MAX_VOLUME;
15203
15204   if (!IN_SCR_FIELD(sx, sy))
15205   {
15206     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15207     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15208
15209     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15210   }
15211
15212   stereo_position = (SOUND_MAX_LEFT +
15213                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15214                      (SCR_FIELDX + 2 * max_distance));
15215
15216   if (IS_LOOP_SOUND(nr))
15217   {
15218     /* This assures that quieter loop sounds do not overwrite louder ones,
15219        while restarting sound volume comparison with each new game frame. */
15220
15221     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15222       return;
15223
15224     loop_sound_volume[nr] = volume;
15225     loop_sound_frame[nr] = FrameCounter;
15226   }
15227
15228   PlaySoundExt(nr, volume, stereo_position, type);
15229 }
15230
15231 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15232 {
15233   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15234                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15235                  y < LEVELY(BY1) ? LEVELY(BY1) :
15236                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15237                  sound_action);
15238 }
15239
15240 static void PlayLevelSoundAction(int x, int y, int action)
15241 {
15242   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15243 }
15244
15245 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15246 {
15247   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15248
15249   if (sound_effect != SND_UNDEFINED)
15250     PlayLevelSound(x, y, sound_effect);
15251 }
15252
15253 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15254                                               int action)
15255 {
15256   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15257
15258   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15259     PlayLevelSound(x, y, sound_effect);
15260 }
15261
15262 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15263 {
15264   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15265
15266   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15267     PlayLevelSound(x, y, sound_effect);
15268 }
15269
15270 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15271 {
15272   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15273
15274   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15275     StopSound(sound_effect);
15276 }
15277
15278 static int getLevelMusicNr(void)
15279 {
15280   int level_pos = level_nr - leveldir_current->first_level;
15281
15282   if (levelset.music[level_nr] != MUS_UNDEFINED)
15283     return levelset.music[level_nr];            // from config file
15284   else
15285     return MAP_NOCONF_MUSIC(level_pos);         // from music dir
15286 }
15287
15288 static void FadeLevelSounds(void)
15289 {
15290   FadeSounds();
15291 }
15292
15293 static void FadeLevelMusic(void)
15294 {
15295   int music_nr = getLevelMusicNr();
15296   char *curr_music = getCurrentlyPlayingMusicFilename();
15297   char *next_music = getMusicInfoEntryFilename(music_nr);
15298
15299   if (!strEqual(curr_music, next_music))
15300     FadeMusic();
15301 }
15302
15303 void FadeLevelSoundsAndMusic(void)
15304 {
15305   FadeLevelSounds();
15306   FadeLevelMusic();
15307 }
15308
15309 static void PlayLevelMusic(void)
15310 {
15311   int music_nr = getLevelMusicNr();
15312   char *curr_music = getCurrentlyPlayingMusicFilename();
15313   char *next_music = getMusicInfoEntryFilename(music_nr);
15314
15315   if (!strEqual(curr_music, next_music))
15316     PlayMusicLoop(music_nr);
15317 }
15318
15319 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15320 {
15321   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15322   int offset = 0;
15323   int x = xx - offset;
15324   int y = yy - offset;
15325
15326   switch (sample)
15327   {
15328     case SOUND_blank:
15329       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15330       break;
15331
15332     case SOUND_roll:
15333       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15334       break;
15335
15336     case SOUND_stone:
15337       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15338       break;
15339
15340     case SOUND_nut:
15341       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15342       break;
15343
15344     case SOUND_crack:
15345       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15346       break;
15347
15348     case SOUND_bug:
15349       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15350       break;
15351
15352     case SOUND_tank:
15353       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15354       break;
15355
15356     case SOUND_android_clone:
15357       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15358       break;
15359
15360     case SOUND_android_move:
15361       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15362       break;
15363
15364     case SOUND_spring:
15365       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15366       break;
15367
15368     case SOUND_slurp:
15369       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15370       break;
15371
15372     case SOUND_eater:
15373       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15374       break;
15375
15376     case SOUND_eater_eat:
15377       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15378       break;
15379
15380     case SOUND_alien:
15381       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15382       break;
15383
15384     case SOUND_collect:
15385       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15386       break;
15387
15388     case SOUND_diamond:
15389       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15390       break;
15391
15392     case SOUND_squash:
15393       // !!! CHECK THIS !!!
15394 #if 1
15395       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15396 #else
15397       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15398 #endif
15399       break;
15400
15401     case SOUND_wonderfall:
15402       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15403       break;
15404
15405     case SOUND_drip:
15406       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15407       break;
15408
15409     case SOUND_push:
15410       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15411       break;
15412
15413     case SOUND_dirt:
15414       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15415       break;
15416
15417     case SOUND_acid:
15418       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15419       break;
15420
15421     case SOUND_ball:
15422       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15423       break;
15424
15425     case SOUND_slide:
15426       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15427       break;
15428
15429     case SOUND_wonder:
15430       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15431       break;
15432
15433     case SOUND_door:
15434       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15435       break;
15436
15437     case SOUND_exit_open:
15438       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15439       break;
15440
15441     case SOUND_exit_leave:
15442       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15443       break;
15444
15445     case SOUND_dynamite:
15446       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15447       break;
15448
15449     case SOUND_tick:
15450       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15451       break;
15452
15453     case SOUND_press:
15454       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15455       break;
15456
15457     case SOUND_wheel:
15458       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15459       break;
15460
15461     case SOUND_boom:
15462       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15463       break;
15464
15465     case SOUND_die:
15466       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15467       break;
15468
15469     case SOUND_time:
15470       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15471       break;
15472
15473     default:
15474       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15475       break;
15476   }
15477 }
15478
15479 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15480 {
15481   int element = map_element_SP_to_RND(element_sp);
15482   int action = map_action_SP_to_RND(action_sp);
15483   int offset = (setup.sp_show_border_elements ? 0 : 1);
15484   int x = xx - offset;
15485   int y = yy - offset;
15486
15487   PlayLevelSoundElementAction(x, y, element, action);
15488 }
15489
15490 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15491 {
15492   int element = map_element_MM_to_RND(element_mm);
15493   int action = map_action_MM_to_RND(action_mm);
15494   int offset = 0;
15495   int x = xx - offset;
15496   int y = yy - offset;
15497
15498   if (!IS_MM_ELEMENT(element))
15499     element = EL_MM_DEFAULT;
15500
15501   PlayLevelSoundElementAction(x, y, element, action);
15502 }
15503
15504 void PlaySound_MM(int sound_mm)
15505 {
15506   int sound = map_sound_MM_to_RND(sound_mm);
15507
15508   if (sound == SND_UNDEFINED)
15509     return;
15510
15511   PlaySound(sound);
15512 }
15513
15514 void PlaySoundLoop_MM(int sound_mm)
15515 {
15516   int sound = map_sound_MM_to_RND(sound_mm);
15517
15518   if (sound == SND_UNDEFINED)
15519     return;
15520
15521   PlaySoundLoop(sound);
15522 }
15523
15524 void StopSound_MM(int sound_mm)
15525 {
15526   int sound = map_sound_MM_to_RND(sound_mm);
15527
15528   if (sound == SND_UNDEFINED)
15529     return;
15530
15531   StopSound(sound);
15532 }
15533
15534 void RaiseScore(int value)
15535 {
15536   game.score += value;
15537
15538   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15539
15540   DisplayGameControlValues();
15541 }
15542
15543 void RaiseScoreElement(int element)
15544 {
15545   switch (element)
15546   {
15547     case EL_EMERALD:
15548     case EL_BD_DIAMOND:
15549     case EL_EMERALD_YELLOW:
15550     case EL_EMERALD_RED:
15551     case EL_EMERALD_PURPLE:
15552     case EL_SP_INFOTRON:
15553       RaiseScore(level.score[SC_EMERALD]);
15554       break;
15555     case EL_DIAMOND:
15556       RaiseScore(level.score[SC_DIAMOND]);
15557       break;
15558     case EL_CRYSTAL:
15559       RaiseScore(level.score[SC_CRYSTAL]);
15560       break;
15561     case EL_PEARL:
15562       RaiseScore(level.score[SC_PEARL]);
15563       break;
15564     case EL_BUG:
15565     case EL_BD_BUTTERFLY:
15566     case EL_SP_ELECTRON:
15567       RaiseScore(level.score[SC_BUG]);
15568       break;
15569     case EL_SPACESHIP:
15570     case EL_BD_FIREFLY:
15571     case EL_SP_SNIKSNAK:
15572       RaiseScore(level.score[SC_SPACESHIP]);
15573       break;
15574     case EL_YAMYAM:
15575     case EL_DARK_YAMYAM:
15576       RaiseScore(level.score[SC_YAMYAM]);
15577       break;
15578     case EL_ROBOT:
15579       RaiseScore(level.score[SC_ROBOT]);
15580       break;
15581     case EL_PACMAN:
15582       RaiseScore(level.score[SC_PACMAN]);
15583       break;
15584     case EL_NUT:
15585       RaiseScore(level.score[SC_NUT]);
15586       break;
15587     case EL_DYNAMITE:
15588     case EL_EM_DYNAMITE:
15589     case EL_SP_DISK_RED:
15590     case EL_DYNABOMB_INCREASE_NUMBER:
15591     case EL_DYNABOMB_INCREASE_SIZE:
15592     case EL_DYNABOMB_INCREASE_POWER:
15593       RaiseScore(level.score[SC_DYNAMITE]);
15594       break;
15595     case EL_SHIELD_NORMAL:
15596     case EL_SHIELD_DEADLY:
15597       RaiseScore(level.score[SC_SHIELD]);
15598       break;
15599     case EL_EXTRA_TIME:
15600       RaiseScore(level.extra_time_score);
15601       break;
15602     case EL_KEY_1:
15603     case EL_KEY_2:
15604     case EL_KEY_3:
15605     case EL_KEY_4:
15606     case EL_EM_KEY_1:
15607     case EL_EM_KEY_2:
15608     case EL_EM_KEY_3:
15609     case EL_EM_KEY_4:
15610     case EL_EMC_KEY_5:
15611     case EL_EMC_KEY_6:
15612     case EL_EMC_KEY_7:
15613     case EL_EMC_KEY_8:
15614     case EL_DC_KEY_WHITE:
15615       RaiseScore(level.score[SC_KEY]);
15616       break;
15617     default:
15618       RaiseScore(element_info[element].collect_score);
15619       break;
15620   }
15621 }
15622
15623 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15624 {
15625   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15626   {
15627     if (!quick_quit)
15628     {
15629       // prevent short reactivation of overlay buttons while closing door
15630       SetOverlayActive(FALSE);
15631       UnmapGameButtons();
15632
15633       // door may still be open due to skipped or envelope style request
15634       CloseDoor(score_info_tape_play ? DOOR_CLOSE_ALL : DOOR_CLOSE_1);
15635     }
15636
15637     if (network.enabled)
15638       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15639     else
15640     {
15641       if (quick_quit)
15642         FadeSkipNextFadeIn();
15643
15644       SetGameStatus(GAME_MODE_MAIN);
15645
15646       DrawMainMenu();
15647     }
15648   }
15649   else          // continue playing the game
15650   {
15651     if (tape.playing && tape.deactivate_display)
15652       TapeDeactivateDisplayOff(TRUE);
15653
15654     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15655
15656     if (tape.playing && tape.deactivate_display)
15657       TapeDeactivateDisplayOn();
15658   }
15659 }
15660
15661 void RequestQuitGame(boolean escape_key_pressed)
15662 {
15663   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15664   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15665                         level_editor_test_game);
15666   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15667                           quick_quit || score_info_tape_play);
15668
15669   RequestQuitGameExt(skip_request, quick_quit,
15670                      "Do you really want to quit the game?");
15671 }
15672
15673 static char *getRestartGameMessage(void)
15674 {
15675   boolean play_again = hasStartedNetworkGame();
15676   static char message[MAX_OUTPUT_LINESIZE];
15677   char *game_over_text = "Game over!";
15678   char *play_again_text = " Play it again?";
15679
15680   if (level.game_engine_type == GAME_ENGINE_TYPE_MM &&
15681       game_mm.game_over_message != NULL)
15682     game_over_text = game_mm.game_over_message;
15683
15684   snprintf(message, MAX_OUTPUT_LINESIZE, "%s%s", game_over_text,
15685            (play_again ? play_again_text : ""));
15686
15687   return message;
15688 }
15689
15690 static void RequestRestartGame(void)
15691 {
15692   char *message = getRestartGameMessage();
15693   boolean has_started_game = hasStartedNetworkGame();
15694   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15695   int door_state = DOOR_CLOSE_1;
15696
15697   if (Request(message, request_mode | REQ_STAY_OPEN) && has_started_game)
15698   {
15699     CloseDoor(door_state);
15700
15701     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15702   }
15703   else
15704   {
15705     // if game was invoked from level editor, also close tape recorder door
15706     if (level_editor_test_game)
15707       door_state = DOOR_CLOSE_ALL;
15708
15709     CloseDoor(door_state);
15710
15711     SetGameStatus(GAME_MODE_MAIN);
15712
15713     DrawMainMenu();
15714   }
15715 }
15716
15717 boolean CheckRestartGame(void)
15718 {
15719   static int game_over_delay = 0;
15720   int game_over_delay_value = 50;
15721   boolean game_over = checkGameFailed();
15722
15723   if (!game_over)
15724   {
15725     game_over_delay = game_over_delay_value;
15726
15727     return FALSE;
15728   }
15729
15730   if (game_over_delay > 0)
15731   {
15732     if (game_over_delay == game_over_delay_value / 2)
15733       PlaySound(SND_GAME_LOSING);
15734
15735     game_over_delay--;
15736
15737     return FALSE;
15738   }
15739
15740   // do not handle game over if request dialog is already active
15741   if (game.request_active)
15742     return FALSE;
15743
15744   // do not ask to play again if game was never actually played
15745   if (!game.GamePlayed)
15746     return FALSE;
15747
15748   // do not ask to play again if this was disabled in setup menu
15749   if (!setup.ask_on_game_over)
15750     return FALSE;
15751
15752   RequestRestartGame();
15753
15754   return TRUE;
15755 }
15756
15757 boolean checkGameSolved(void)
15758 {
15759   // set for all game engines if level was solved
15760   return game.LevelSolved_GameEnd;
15761 }
15762
15763 boolean checkGameFailed(void)
15764 {
15765   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15766     return (game_em.game_over && !game_em.level_solved);
15767   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15768     return (game_sp.game_over && !game_sp.level_solved);
15769   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15770     return (game_mm.game_over && !game_mm.level_solved);
15771   else                          // GAME_ENGINE_TYPE_RND
15772     return (game.GameOver && !game.LevelSolved);
15773 }
15774
15775 boolean checkGameEnded(void)
15776 {
15777   return (checkGameSolved() || checkGameFailed());
15778 }
15779
15780
15781 // ----------------------------------------------------------------------------
15782 // random generator functions
15783 // ----------------------------------------------------------------------------
15784
15785 unsigned int InitEngineRandom_RND(int seed)
15786 {
15787   game.num_random_calls = 0;
15788
15789   return InitEngineRandom(seed);
15790 }
15791
15792 unsigned int RND(int max)
15793 {
15794   if (max > 0)
15795   {
15796     game.num_random_calls++;
15797
15798     return GetEngineRandom(max);
15799   }
15800
15801   return 0;
15802 }
15803
15804
15805 // ----------------------------------------------------------------------------
15806 // game engine snapshot handling functions
15807 // ----------------------------------------------------------------------------
15808
15809 struct EngineSnapshotInfo
15810 {
15811   // runtime values for custom element collect score
15812   int collect_score[NUM_CUSTOM_ELEMENTS];
15813
15814   // runtime values for group element choice position
15815   int choice_pos[NUM_GROUP_ELEMENTS];
15816
15817   // runtime values for belt position animations
15818   int belt_graphic[4][NUM_BELT_PARTS];
15819   int belt_anim_mode[4][NUM_BELT_PARTS];
15820 };
15821
15822 static struct EngineSnapshotInfo engine_snapshot_rnd;
15823 static char *snapshot_level_identifier = NULL;
15824 static int snapshot_level_nr = -1;
15825
15826 static void SaveEngineSnapshotValues_RND(void)
15827 {
15828   static int belt_base_active_element[4] =
15829   {
15830     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15831     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15832     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15833     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15834   };
15835   int i, j;
15836
15837   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15838   {
15839     int element = EL_CUSTOM_START + i;
15840
15841     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15842   }
15843
15844   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15845   {
15846     int element = EL_GROUP_START + i;
15847
15848     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15849   }
15850
15851   for (i = 0; i < 4; i++)
15852   {
15853     for (j = 0; j < NUM_BELT_PARTS; j++)
15854     {
15855       int element = belt_base_active_element[i] + j;
15856       int graphic = el2img(element);
15857       int anim_mode = graphic_info[graphic].anim_mode;
15858
15859       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15860       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15861     }
15862   }
15863 }
15864
15865 static void LoadEngineSnapshotValues_RND(void)
15866 {
15867   unsigned int num_random_calls = game.num_random_calls;
15868   int i, j;
15869
15870   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15871   {
15872     int element = EL_CUSTOM_START + i;
15873
15874     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15875   }
15876
15877   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15878   {
15879     int element = EL_GROUP_START + i;
15880
15881     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15882   }
15883
15884   for (i = 0; i < 4; i++)
15885   {
15886     for (j = 0; j < NUM_BELT_PARTS; j++)
15887     {
15888       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15889       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15890
15891       graphic_info[graphic].anim_mode = anim_mode;
15892     }
15893   }
15894
15895   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15896   {
15897     InitRND(tape.random_seed);
15898     for (i = 0; i < num_random_calls; i++)
15899       RND(1);
15900   }
15901
15902   if (game.num_random_calls != num_random_calls)
15903   {
15904     Error("number of random calls out of sync");
15905     Error("number of random calls should be %d", num_random_calls);
15906     Error("number of random calls is %d", game.num_random_calls);
15907
15908     Fail("this should not happen -- please debug");
15909   }
15910 }
15911
15912 void FreeEngineSnapshotSingle(void)
15913 {
15914   FreeSnapshotSingle();
15915
15916   setString(&snapshot_level_identifier, NULL);
15917   snapshot_level_nr = -1;
15918 }
15919
15920 void FreeEngineSnapshotList(void)
15921 {
15922   FreeSnapshotList();
15923 }
15924
15925 static ListNode *SaveEngineSnapshotBuffers(void)
15926 {
15927   ListNode *buffers = NULL;
15928
15929   // copy some special values to a structure better suited for the snapshot
15930
15931   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15932     SaveEngineSnapshotValues_RND();
15933   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15934     SaveEngineSnapshotValues_EM();
15935   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15936     SaveEngineSnapshotValues_SP(&buffers);
15937   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15938     SaveEngineSnapshotValues_MM();
15939
15940   // save values stored in special snapshot structure
15941
15942   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15943     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15944   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15945     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15946   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15947     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15948   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15949     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15950
15951   // save further RND engine values
15952
15953   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15954   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15955   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15956
15957   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15958   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15959   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15960   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15961   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15962
15963   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15964   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15965   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15966
15967   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15968
15969   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15970   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15971
15972   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15973   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15974   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15975   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15976   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15977   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15978   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15979   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15980   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15981   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15982   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15983   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15984   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15985   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15986   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15987   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15988   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15989   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15990
15991   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15992   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15993
15994   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15995   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15996   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15997
15998   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15999   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16000
16001   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16002   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16003   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
16004   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16005   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16006   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16007
16008   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16009   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16010
16011 #if 0
16012   ListNode *node = engine_snapshot_list_rnd;
16013   int num_bytes = 0;
16014
16015   while (node != NULL)
16016   {
16017     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16018
16019     node = node->next;
16020   }
16021
16022   Debug("game:playing:SaveEngineSnapshotBuffers",
16023         "size of engine snapshot: %d bytes", num_bytes);
16024 #endif
16025
16026   return buffers;
16027 }
16028
16029 void SaveEngineSnapshotSingle(void)
16030 {
16031   ListNode *buffers = SaveEngineSnapshotBuffers();
16032
16033   // finally save all snapshot buffers to single snapshot
16034   SaveSnapshotSingle(buffers);
16035
16036   // save level identification information
16037   setString(&snapshot_level_identifier, leveldir_current->identifier);
16038   snapshot_level_nr = level_nr;
16039 }
16040
16041 boolean CheckSaveEngineSnapshotToList(void)
16042 {
16043   boolean save_snapshot =
16044     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
16045      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
16046       game.snapshot.changed_action) ||
16047      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16048       game.snapshot.collected_item));
16049
16050   game.snapshot.changed_action = FALSE;
16051   game.snapshot.collected_item = FALSE;
16052   game.snapshot.save_snapshot = save_snapshot;
16053
16054   return save_snapshot;
16055 }
16056
16057 void SaveEngineSnapshotToList(void)
16058 {
16059   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
16060       tape.quick_resume)
16061     return;
16062
16063   ListNode *buffers = SaveEngineSnapshotBuffers();
16064
16065   // finally save all snapshot buffers to snapshot list
16066   SaveSnapshotToList(buffers);
16067 }
16068
16069 void SaveEngineSnapshotToListInitial(void)
16070 {
16071   FreeEngineSnapshotList();
16072
16073   SaveEngineSnapshotToList();
16074 }
16075
16076 static void LoadEngineSnapshotValues(void)
16077 {
16078   // restore special values from snapshot structure
16079
16080   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16081     LoadEngineSnapshotValues_RND();
16082   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16083     LoadEngineSnapshotValues_EM();
16084   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16085     LoadEngineSnapshotValues_SP();
16086   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16087     LoadEngineSnapshotValues_MM();
16088 }
16089
16090 void LoadEngineSnapshotSingle(void)
16091 {
16092   LoadSnapshotSingle();
16093
16094   LoadEngineSnapshotValues();
16095 }
16096
16097 static void LoadEngineSnapshot_Undo(int steps)
16098 {
16099   LoadSnapshotFromList_Older(steps);
16100
16101   LoadEngineSnapshotValues();
16102 }
16103
16104 static void LoadEngineSnapshot_Redo(int steps)
16105 {
16106   LoadSnapshotFromList_Newer(steps);
16107
16108   LoadEngineSnapshotValues();
16109 }
16110
16111 boolean CheckEngineSnapshotSingle(void)
16112 {
16113   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16114           snapshot_level_nr == level_nr);
16115 }
16116
16117 boolean CheckEngineSnapshotList(void)
16118 {
16119   return CheckSnapshotList();
16120 }
16121
16122
16123 // ---------- new game button stuff -------------------------------------------
16124
16125 static struct
16126 {
16127   int graphic;
16128   struct XY *pos;
16129   int gadget_id;
16130   boolean *setup_value;
16131   boolean allowed_on_tape;
16132   boolean is_touch_button;
16133   char *infotext;
16134 } gamebutton_info[NUM_GAME_BUTTONS] =
16135 {
16136   {
16137     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16138     GAME_CTRL_ID_STOP,                          NULL,
16139     TRUE, FALSE,                                "stop game"
16140   },
16141   {
16142     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16143     GAME_CTRL_ID_PAUSE,                         NULL,
16144     TRUE, FALSE,                                "pause game"
16145   },
16146   {
16147     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16148     GAME_CTRL_ID_PLAY,                          NULL,
16149     TRUE, FALSE,                                "play game"
16150   },
16151   {
16152     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16153     GAME_CTRL_ID_UNDO,                          NULL,
16154     TRUE, FALSE,                                "undo step"
16155   },
16156   {
16157     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16158     GAME_CTRL_ID_REDO,                          NULL,
16159     TRUE, FALSE,                                "redo step"
16160   },
16161   {
16162     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16163     GAME_CTRL_ID_SAVE,                          NULL,
16164     TRUE, FALSE,                                "save game"
16165   },
16166   {
16167     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16168     GAME_CTRL_ID_PAUSE2,                        NULL,
16169     TRUE, FALSE,                                "pause game"
16170   },
16171   {
16172     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16173     GAME_CTRL_ID_LOAD,                          NULL,
16174     TRUE, FALSE,                                "load game"
16175   },
16176   {
16177     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16178     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16179     FALSE, FALSE,                               "stop game"
16180   },
16181   {
16182     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16183     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16184     FALSE, FALSE,                               "pause game"
16185   },
16186   {
16187     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16188     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16189     FALSE, FALSE,                               "play game"
16190   },
16191   {
16192     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16193     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16194     FALSE, TRUE,                                "stop game"
16195   },
16196   {
16197     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16198     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16199     FALSE, TRUE,                                "pause game"
16200   },
16201   {
16202     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16203     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16204     TRUE, FALSE,                                "background music on/off"
16205   },
16206   {
16207     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16208     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16209     TRUE, FALSE,                                "sound loops on/off"
16210   },
16211   {
16212     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16213     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16214     TRUE, FALSE,                                "normal sounds on/off"
16215   },
16216   {
16217     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16218     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16219     FALSE, FALSE,                               "background music on/off"
16220   },
16221   {
16222     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16223     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16224     FALSE, FALSE,                               "sound loops on/off"
16225   },
16226   {
16227     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16228     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16229     FALSE, FALSE,                               "normal sounds on/off"
16230   }
16231 };
16232
16233 void CreateGameButtons(void)
16234 {
16235   int i;
16236
16237   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16238   {
16239     int graphic = gamebutton_info[i].graphic;
16240     struct GraphicInfo *gfx = &graphic_info[graphic];
16241     struct XY *pos = gamebutton_info[i].pos;
16242     struct GadgetInfo *gi;
16243     int button_type;
16244     boolean checked;
16245     unsigned int event_mask;
16246     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16247     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16248     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16249     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16250     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16251     int gd_x   = gfx->src_x;
16252     int gd_y   = gfx->src_y;
16253     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16254     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16255     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16256     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16257     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16258     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16259     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16260     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16261     int id = i;
16262
16263     // do not use touch buttons if overlay touch buttons are disabled
16264     if (is_touch_button && !setup.touch.overlay_buttons)
16265       continue;
16266
16267     if (gfx->bitmap == NULL)
16268     {
16269       game_gadget[id] = NULL;
16270
16271       continue;
16272     }
16273
16274     if (id == GAME_CTRL_ID_STOP ||
16275         id == GAME_CTRL_ID_PANEL_STOP ||
16276         id == GAME_CTRL_ID_TOUCH_STOP ||
16277         id == GAME_CTRL_ID_PLAY ||
16278         id == GAME_CTRL_ID_PANEL_PLAY ||
16279         id == GAME_CTRL_ID_SAVE ||
16280         id == GAME_CTRL_ID_LOAD)
16281     {
16282       button_type = GD_TYPE_NORMAL_BUTTON;
16283       checked = FALSE;
16284       event_mask = GD_EVENT_RELEASED;
16285     }
16286     else if (id == GAME_CTRL_ID_UNDO ||
16287              id == GAME_CTRL_ID_REDO)
16288     {
16289       button_type = GD_TYPE_NORMAL_BUTTON;
16290       checked = FALSE;
16291       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16292     }
16293     else
16294     {
16295       button_type = GD_TYPE_CHECK_BUTTON;
16296       checked = (gamebutton_info[i].setup_value != NULL ?
16297                  *gamebutton_info[i].setup_value : FALSE);
16298       event_mask = GD_EVENT_PRESSED;
16299     }
16300
16301     gi = CreateGadget(GDI_CUSTOM_ID, id,
16302                       GDI_IMAGE_ID, graphic,
16303                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16304                       GDI_X, base_x + x,
16305                       GDI_Y, base_y + y,
16306                       GDI_WIDTH, gfx->width,
16307                       GDI_HEIGHT, gfx->height,
16308                       GDI_TYPE, button_type,
16309                       GDI_STATE, GD_BUTTON_UNPRESSED,
16310                       GDI_CHECKED, checked,
16311                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16312                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16313                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16314                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16315                       GDI_DIRECT_DRAW, FALSE,
16316                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16317                       GDI_EVENT_MASK, event_mask,
16318                       GDI_CALLBACK_ACTION, HandleGameButtons,
16319                       GDI_END);
16320
16321     if (gi == NULL)
16322       Fail("cannot create gadget");
16323
16324     game_gadget[id] = gi;
16325   }
16326 }
16327
16328 void FreeGameButtons(void)
16329 {
16330   int i;
16331
16332   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16333     FreeGadget(game_gadget[i]);
16334 }
16335
16336 static void UnmapGameButtonsAtSamePosition(int id)
16337 {
16338   int i;
16339
16340   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16341     if (i != id &&
16342         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16343         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16344       UnmapGadget(game_gadget[i]);
16345 }
16346
16347 static void UnmapGameButtonsAtSamePosition_All(void)
16348 {
16349   if (setup.show_load_save_buttons)
16350   {
16351     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16352     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16353     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16354   }
16355   else if (setup.show_undo_redo_buttons)
16356   {
16357     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16358     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16359     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16360   }
16361   else
16362   {
16363     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16364     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16365     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16366
16367     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16368     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16369     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16370   }
16371 }
16372
16373 void MapLoadSaveButtons(void)
16374 {
16375   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16376   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16377
16378   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
16379   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
16380 }
16381
16382 void MapUndoRedoButtons(void)
16383 {
16384   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16385   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16386
16387   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16388   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16389 }
16390
16391 void ModifyPauseButtons(void)
16392 {
16393   static int ids[] =
16394   {
16395     GAME_CTRL_ID_PAUSE,
16396     GAME_CTRL_ID_PAUSE2,
16397     GAME_CTRL_ID_PANEL_PAUSE,
16398     GAME_CTRL_ID_TOUCH_PAUSE,
16399     -1
16400   };
16401   int i;
16402
16403   for (i = 0; ids[i] > -1; i++)
16404     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16405 }
16406
16407 static void MapGameButtonsExt(boolean on_tape)
16408 {
16409   int i;
16410
16411   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16412   {
16413     if ((i == GAME_CTRL_ID_UNDO ||
16414          i == GAME_CTRL_ID_REDO) &&
16415         game_status != GAME_MODE_PLAYING)
16416       continue;
16417
16418     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16419       MapGadget(game_gadget[i]);
16420   }
16421
16422   UnmapGameButtonsAtSamePosition_All();
16423
16424   RedrawGameButtons();
16425 }
16426
16427 static void UnmapGameButtonsExt(boolean on_tape)
16428 {
16429   int i;
16430
16431   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16432     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16433       UnmapGadget(game_gadget[i]);
16434 }
16435
16436 static void RedrawGameButtonsExt(boolean on_tape)
16437 {
16438   int i;
16439
16440   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16441     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16442       RedrawGadget(game_gadget[i]);
16443 }
16444
16445 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16446 {
16447   if (gi == NULL)
16448     return;
16449
16450   gi->checked = state;
16451 }
16452
16453 static void RedrawSoundButtonGadget(int id)
16454 {
16455   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16456              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16457              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16458              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16459              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16460              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16461              id);
16462
16463   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16464   RedrawGadget(game_gadget[id2]);
16465 }
16466
16467 void MapGameButtons(void)
16468 {
16469   MapGameButtonsExt(FALSE);
16470 }
16471
16472 void UnmapGameButtons(void)
16473 {
16474   UnmapGameButtonsExt(FALSE);
16475 }
16476
16477 void RedrawGameButtons(void)
16478 {
16479   RedrawGameButtonsExt(FALSE);
16480 }
16481
16482 void MapGameButtonsOnTape(void)
16483 {
16484   MapGameButtonsExt(TRUE);
16485 }
16486
16487 void UnmapGameButtonsOnTape(void)
16488 {
16489   UnmapGameButtonsExt(TRUE);
16490 }
16491
16492 void RedrawGameButtonsOnTape(void)
16493 {
16494   RedrawGameButtonsExt(TRUE);
16495 }
16496
16497 static void GameUndoRedoExt(void)
16498 {
16499   ClearPlayerAction();
16500
16501   tape.pausing = TRUE;
16502
16503   RedrawPlayfield();
16504   UpdateAndDisplayGameControlValues();
16505
16506   DrawCompleteVideoDisplay();
16507   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16508   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16509   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16510
16511   ModifyPauseButtons();
16512
16513   BackToFront();
16514 }
16515
16516 static void GameUndo(int steps)
16517 {
16518   if (!CheckEngineSnapshotList())
16519     return;
16520
16521   int tape_property_bits = tape.property_bits;
16522
16523   LoadEngineSnapshot_Undo(steps);
16524
16525   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16526
16527   GameUndoRedoExt();
16528 }
16529
16530 static void GameRedo(int steps)
16531 {
16532   if (!CheckEngineSnapshotList())
16533     return;
16534
16535   int tape_property_bits = tape.property_bits;
16536
16537   LoadEngineSnapshot_Redo(steps);
16538
16539   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16540
16541   GameUndoRedoExt();
16542 }
16543
16544 static void HandleGameButtonsExt(int id, int button)
16545 {
16546   static boolean game_undo_executed = FALSE;
16547   int steps = BUTTON_STEPSIZE(button);
16548   boolean handle_game_buttons =
16549     (game_status == GAME_MODE_PLAYING ||
16550      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16551
16552   if (!handle_game_buttons)
16553     return;
16554
16555   switch (id)
16556   {
16557     case GAME_CTRL_ID_STOP:
16558     case GAME_CTRL_ID_PANEL_STOP:
16559     case GAME_CTRL_ID_TOUCH_STOP:
16560       TapeStopGame();
16561
16562       break;
16563
16564     case GAME_CTRL_ID_PAUSE:
16565     case GAME_CTRL_ID_PAUSE2:
16566     case GAME_CTRL_ID_PANEL_PAUSE:
16567     case GAME_CTRL_ID_TOUCH_PAUSE:
16568       if (network.enabled && game_status == GAME_MODE_PLAYING)
16569       {
16570         if (tape.pausing)
16571           SendToServer_ContinuePlaying();
16572         else
16573           SendToServer_PausePlaying();
16574       }
16575       else
16576         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16577
16578       game_undo_executed = FALSE;
16579
16580       break;
16581
16582     case GAME_CTRL_ID_PLAY:
16583     case GAME_CTRL_ID_PANEL_PLAY:
16584       if (game_status == GAME_MODE_MAIN)
16585       {
16586         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16587       }
16588       else if (tape.pausing)
16589       {
16590         if (network.enabled)
16591           SendToServer_ContinuePlaying();
16592         else
16593           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16594       }
16595       break;
16596
16597     case GAME_CTRL_ID_UNDO:
16598       // Important: When using "save snapshot when collecting an item" mode,
16599       // load last (current) snapshot for first "undo" after pressing "pause"
16600       // (else the last-but-one snapshot would be loaded, because the snapshot
16601       // pointer already points to the last snapshot when pressing "pause",
16602       // which is fine for "every step/move" mode, but not for "every collect")
16603       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16604           !game_undo_executed)
16605         steps--;
16606
16607       game_undo_executed = TRUE;
16608
16609       GameUndo(steps);
16610       break;
16611
16612     case GAME_CTRL_ID_REDO:
16613       GameRedo(steps);
16614       break;
16615
16616     case GAME_CTRL_ID_SAVE:
16617       TapeQuickSave();
16618       break;
16619
16620     case GAME_CTRL_ID_LOAD:
16621       TapeQuickLoad();
16622       break;
16623
16624     case SOUND_CTRL_ID_MUSIC:
16625     case SOUND_CTRL_ID_PANEL_MUSIC:
16626       if (setup.sound_music)
16627       { 
16628         setup.sound_music = FALSE;
16629
16630         FadeMusic();
16631       }
16632       else if (audio.music_available)
16633       { 
16634         setup.sound = setup.sound_music = TRUE;
16635
16636         SetAudioMode(setup.sound);
16637
16638         if (game_status == GAME_MODE_PLAYING)
16639           PlayLevelMusic();
16640       }
16641
16642       RedrawSoundButtonGadget(id);
16643
16644       break;
16645
16646     case SOUND_CTRL_ID_LOOPS:
16647     case SOUND_CTRL_ID_PANEL_LOOPS:
16648       if (setup.sound_loops)
16649         setup.sound_loops = FALSE;
16650       else if (audio.loops_available)
16651       {
16652         setup.sound = setup.sound_loops = TRUE;
16653
16654         SetAudioMode(setup.sound);
16655       }
16656
16657       RedrawSoundButtonGadget(id);
16658
16659       break;
16660
16661     case SOUND_CTRL_ID_SIMPLE:
16662     case SOUND_CTRL_ID_PANEL_SIMPLE:
16663       if (setup.sound_simple)
16664         setup.sound_simple = FALSE;
16665       else if (audio.sound_available)
16666       {
16667         setup.sound = setup.sound_simple = TRUE;
16668
16669         SetAudioMode(setup.sound);
16670       }
16671
16672       RedrawSoundButtonGadget(id);
16673
16674       break;
16675
16676     default:
16677       break;
16678   }
16679 }
16680
16681 static void HandleGameButtons(struct GadgetInfo *gi)
16682 {
16683   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16684 }
16685
16686 void HandleSoundButtonKeys(Key key)
16687 {
16688   if (key == setup.shortcut.sound_simple)
16689     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16690   else if (key == setup.shortcut.sound_loops)
16691     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16692   else if (key == setup.shortcut.sound_music)
16693     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16694 }