added setting scroll offset to global animation position triggered by CE
[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     SetGameStatus(GAME_MODE_PLAYING);
3633   }
3634
3635   if (level_editor_test_game)
3636     FadeSkipNextFadeIn();
3637
3638   // needed if different viewport properties defined for playing
3639   ChangeViewportPropertiesIfNeeded();
3640
3641   ClearField();
3642
3643   DrawCompleteVideoDisplay();
3644
3645   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3646
3647   InitGameEngine();
3648   InitGameControlValues();
3649
3650   if (tape.recording)
3651   {
3652     // initialize tape actions from game when recording tape
3653     tape.use_key_actions   = game.use_key_actions;
3654     tape.use_mouse_actions = game.use_mouse_actions;
3655
3656     // initialize visible playfield size when recording tape (for team mode)
3657     tape.scr_fieldx = SCR_FIELDX;
3658     tape.scr_fieldy = SCR_FIELDY;
3659   }
3660
3661   // don't play tapes over network
3662   network_playing = (network.enabled && !tape.playing);
3663
3664   for (i = 0; i < MAX_PLAYERS; i++)
3665   {
3666     struct PlayerInfo *player = &stored_player[i];
3667
3668     player->index_nr = i;
3669     player->index_bit = (1 << i);
3670     player->element_nr = EL_PLAYER_1 + i;
3671
3672     player->present = FALSE;
3673     player->active = FALSE;
3674     player->mapped = FALSE;
3675
3676     player->killed = FALSE;
3677     player->reanimated = FALSE;
3678     player->buried = FALSE;
3679
3680     player->action = 0;
3681     player->effective_action = 0;
3682     player->programmed_action = 0;
3683     player->snap_action = 0;
3684
3685     player->mouse_action.lx = 0;
3686     player->mouse_action.ly = 0;
3687     player->mouse_action.button = 0;
3688     player->mouse_action.button_hint = 0;
3689
3690     player->effective_mouse_action.lx = 0;
3691     player->effective_mouse_action.ly = 0;
3692     player->effective_mouse_action.button = 0;
3693     player->effective_mouse_action.button_hint = 0;
3694
3695     for (j = 0; j < MAX_NUM_KEYS; j++)
3696       player->key[j] = FALSE;
3697
3698     player->num_white_keys = 0;
3699
3700     player->dynabomb_count = 0;
3701     player->dynabomb_size = 1;
3702     player->dynabombs_left = 0;
3703     player->dynabomb_xl = FALSE;
3704
3705     player->MovDir = initial_move_dir;
3706     player->MovPos = 0;
3707     player->GfxPos = 0;
3708     player->GfxDir = initial_move_dir;
3709     player->GfxAction = ACTION_DEFAULT;
3710     player->Frame = 0;
3711     player->StepFrame = 0;
3712
3713     player->initial_element = player->element_nr;
3714     player->artwork_element =
3715       (level.use_artwork_element[i] ? level.artwork_element[i] :
3716        player->element_nr);
3717     player->use_murphy = FALSE;
3718
3719     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3720     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3721
3722     player->gravity = level.initial_player_gravity[i];
3723
3724     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3725
3726     player->actual_frame_counter.count = 0;
3727     player->actual_frame_counter.value = 1;
3728
3729     player->step_counter = 0;
3730
3731     player->last_move_dir = initial_move_dir;
3732
3733     player->is_active = FALSE;
3734
3735     player->is_waiting = FALSE;
3736     player->is_moving = FALSE;
3737     player->is_auto_moving = FALSE;
3738     player->is_digging = FALSE;
3739     player->is_snapping = FALSE;
3740     player->is_collecting = FALSE;
3741     player->is_pushing = FALSE;
3742     player->is_switching = FALSE;
3743     player->is_dropping = FALSE;
3744     player->is_dropping_pressed = FALSE;
3745
3746     player->is_bored = FALSE;
3747     player->is_sleeping = FALSE;
3748
3749     player->was_waiting = TRUE;
3750     player->was_moving = FALSE;
3751     player->was_snapping = FALSE;
3752     player->was_dropping = FALSE;
3753
3754     player->force_dropping = FALSE;
3755
3756     player->frame_counter_bored = -1;
3757     player->frame_counter_sleeping = -1;
3758
3759     player->anim_delay_counter = 0;
3760     player->post_delay_counter = 0;
3761
3762     player->dir_waiting = initial_move_dir;
3763     player->action_waiting = ACTION_DEFAULT;
3764     player->last_action_waiting = ACTION_DEFAULT;
3765     player->special_action_bored = ACTION_DEFAULT;
3766     player->special_action_sleeping = ACTION_DEFAULT;
3767
3768     player->switch_x = -1;
3769     player->switch_y = -1;
3770
3771     player->drop_x = -1;
3772     player->drop_y = -1;
3773
3774     player->show_envelope = 0;
3775
3776     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3777
3778     player->push_delay       = -1;      // initialized when pushing starts
3779     player->push_delay_value = game.initial_push_delay_value;
3780
3781     player->drop_delay = 0;
3782     player->drop_pressed_delay = 0;
3783
3784     player->last_jx = -1;
3785     player->last_jy = -1;
3786     player->jx = -1;
3787     player->jy = -1;
3788
3789     player->shield_normal_time_left = 0;
3790     player->shield_deadly_time_left = 0;
3791
3792     player->last_removed_element = EL_UNDEFINED;
3793
3794     player->inventory_infinite_element = EL_UNDEFINED;
3795     player->inventory_size = 0;
3796
3797     if (level.use_initial_inventory[i])
3798     {
3799       for (j = 0; j < level.initial_inventory_size[i]; j++)
3800       {
3801         int element = level.initial_inventory_content[i][j];
3802         int collect_count = element_info[element].collect_count_initial;
3803         int k;
3804
3805         if (!IS_CUSTOM_ELEMENT(element))
3806           collect_count = 1;
3807
3808         if (collect_count == 0)
3809           player->inventory_infinite_element = element;
3810         else
3811           for (k = 0; k < collect_count; k++)
3812             if (player->inventory_size < MAX_INVENTORY_SIZE)
3813               player->inventory_element[player->inventory_size++] = element;
3814       }
3815     }
3816
3817     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3818     SnapField(player, 0, 0);
3819
3820     map_player_action[i] = i;
3821   }
3822
3823   network_player_action_received = FALSE;
3824
3825   // initial null action
3826   if (network_playing)
3827     SendToServer_MovePlayer(MV_NONE);
3828
3829   FrameCounter = 0;
3830   TimeFrames = 0;
3831   TimePlayed = 0;
3832   TimeLeft = level.time;
3833   TapeTime = 0;
3834
3835   ScreenMovDir = MV_NONE;
3836   ScreenMovPos = 0;
3837   ScreenGfxPos = 0;
3838
3839   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3840
3841   game.robot_wheel_x = -1;
3842   game.robot_wheel_y = -1;
3843
3844   game.exit_x = -1;
3845   game.exit_y = -1;
3846
3847   game.all_players_gone = FALSE;
3848
3849   game.LevelSolved = FALSE;
3850   game.GameOver = FALSE;
3851
3852   game.GamePlayed = !tape.playing;
3853
3854   game.LevelSolved_GameWon = FALSE;
3855   game.LevelSolved_GameEnd = FALSE;
3856   game.LevelSolved_SaveTape = FALSE;
3857   game.LevelSolved_SaveScore = FALSE;
3858
3859   game.LevelSolved_CountingTime = 0;
3860   game.LevelSolved_CountingScore = 0;
3861   game.LevelSolved_CountingHealth = 0;
3862
3863   game.panel.active = TRUE;
3864
3865   game.no_level_time_limit = (level.time == 0);
3866   game.time_limit = (leveldir_current->time_limit && setup.time_limit);
3867
3868   game.yamyam_content_nr = 0;
3869   game.robot_wheel_active = FALSE;
3870   game.magic_wall_active = FALSE;
3871   game.magic_wall_time_left = 0;
3872   game.light_time_left = 0;
3873   game.timegate_time_left = 0;
3874   game.switchgate_pos = 0;
3875   game.wind_direction = level.wind_direction_initial;
3876
3877   game.time_final = 0;
3878   game.score_time_final = 0;
3879
3880   game.score = 0;
3881   game.score_final = 0;
3882
3883   game.health = MAX_HEALTH;
3884   game.health_final = MAX_HEALTH;
3885
3886   game.gems_still_needed = level.gems_needed;
3887   game.sokoban_fields_still_needed = 0;
3888   game.sokoban_objects_still_needed = 0;
3889   game.lights_still_needed = 0;
3890   game.players_still_needed = 0;
3891   game.friends_still_needed = 0;
3892
3893   game.lenses_time_left = 0;
3894   game.magnify_time_left = 0;
3895
3896   game.ball_active = level.ball_active_initial;
3897   game.ball_content_nr = 0;
3898
3899   game.explosions_delayed = TRUE;
3900
3901   game.envelope_active = FALSE;
3902
3903   // special case: set custom artwork setting to initial value
3904   game.use_masked_elements = game.use_masked_elements_initial;
3905
3906   for (i = 0; i < NUM_BELTS; i++)
3907   {
3908     game.belt_dir[i] = MV_NONE;
3909     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3910   }
3911
3912   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3913     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3914
3915 #if DEBUG_INIT_PLAYER
3916   DebugPrintPlayerStatus("Player status at level initialization");
3917 #endif
3918
3919   SCAN_PLAYFIELD(x, y)
3920   {
3921     Tile[x][y] = Last[x][y] = level.field[x][y];
3922     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3923     ChangeDelay[x][y] = 0;
3924     ChangePage[x][y] = -1;
3925     CustomValue[x][y] = 0;              // initialized in InitField()
3926     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3927     AmoebaNr[x][y] = 0;
3928     WasJustMoving[x][y] = 0;
3929     WasJustFalling[x][y] = 0;
3930     CheckCollision[x][y] = 0;
3931     CheckImpact[x][y] = 0;
3932     Stop[x][y] = FALSE;
3933     Pushed[x][y] = FALSE;
3934
3935     ChangeCount[x][y] = 0;
3936     ChangeEvent[x][y] = -1;
3937
3938     ExplodePhase[x][y] = 0;
3939     ExplodeDelay[x][y] = 0;
3940     ExplodeField[x][y] = EX_TYPE_NONE;
3941
3942     RunnerVisit[x][y] = 0;
3943     PlayerVisit[x][y] = 0;
3944
3945     GfxFrame[x][y] = 0;
3946     GfxRandom[x][y] = INIT_GFX_RANDOM();
3947     GfxRandomStatic[x][y] = INIT_GFX_RANDOM();
3948     GfxElement[x][y] = EL_UNDEFINED;
3949     GfxElementEmpty[x][y] = EL_EMPTY;
3950     GfxAction[x][y] = ACTION_DEFAULT;
3951     GfxDir[x][y] = MV_NONE;
3952     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3953   }
3954
3955   SCAN_PLAYFIELD(x, y)
3956   {
3957     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3958       emulate_bd = FALSE;
3959     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3960       emulate_sp = FALSE;
3961
3962     InitField(x, y, TRUE);
3963
3964     ResetGfxAnimation(x, y);
3965   }
3966
3967   InitBeltMovement();
3968
3969   for (i = 0; i < MAX_PLAYERS; i++)
3970   {
3971     struct PlayerInfo *player = &stored_player[i];
3972
3973     // set number of special actions for bored and sleeping animation
3974     player->num_special_action_bored =
3975       get_num_special_action(player->artwork_element,
3976                              ACTION_BORING_1, ACTION_BORING_LAST);
3977     player->num_special_action_sleeping =
3978       get_num_special_action(player->artwork_element,
3979                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3980   }
3981
3982   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3983                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3984
3985   // initialize type of slippery elements
3986   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3987   {
3988     if (!IS_CUSTOM_ELEMENT(i))
3989     {
3990       // default: elements slip down either to the left or right randomly
3991       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3992
3993       // SP style elements prefer to slip down on the left side
3994       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3995         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3996
3997       // BD style elements prefer to slip down on the left side
3998       if (game.emulation == EMU_BOULDERDASH)
3999         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4000     }
4001   }
4002
4003   // initialize explosion and ignition delay
4004   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4005   {
4006     if (!IS_CUSTOM_ELEMENT(i))
4007     {
4008       int num_phase = 8;
4009       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4010                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4011                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
4012       int last_phase = (num_phase + 1) * delay;
4013       int half_phase = (num_phase / 2) * delay;
4014
4015       element_info[i].explosion_delay = last_phase - 1;
4016       element_info[i].ignition_delay = half_phase;
4017
4018       if (i == EL_BLACK_ORB)
4019         element_info[i].ignition_delay = 1;
4020     }
4021   }
4022
4023   // correct non-moving belts to start moving left
4024   for (i = 0; i < NUM_BELTS; i++)
4025     if (game.belt_dir[i] == MV_NONE)
4026       game.belt_dir_nr[i] = 3;          // not moving, next moving left
4027
4028 #if USE_NEW_PLAYER_ASSIGNMENTS
4029   // use preferred player also in local single-player mode
4030   if (!network.enabled && !game.team_mode)
4031   {
4032     int new_index_nr = setup.network_player_nr;
4033
4034     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
4035     {
4036       for (i = 0; i < MAX_PLAYERS; i++)
4037         stored_player[i].connected_locally = FALSE;
4038
4039       stored_player[new_index_nr].connected_locally = TRUE;
4040     }
4041   }
4042
4043   for (i = 0; i < MAX_PLAYERS; i++)
4044   {
4045     stored_player[i].connected = FALSE;
4046
4047     // in network game mode, the local player might not be the first player
4048     if (stored_player[i].connected_locally)
4049       local_player = &stored_player[i];
4050   }
4051
4052   if (!network.enabled)
4053     local_player->connected = TRUE;
4054
4055   if (tape.playing)
4056   {
4057     for (i = 0; i < MAX_PLAYERS; i++)
4058       stored_player[i].connected = tape.player_participates[i];
4059   }
4060   else if (network.enabled)
4061   {
4062     // add team mode players connected over the network (needed for correct
4063     // assignment of player figures from level to locally playing players)
4064
4065     for (i = 0; i < MAX_PLAYERS; i++)
4066       if (stored_player[i].connected_network)
4067         stored_player[i].connected = TRUE;
4068   }
4069   else if (game.team_mode)
4070   {
4071     // try to guess locally connected team mode players (needed for correct
4072     // assignment of player figures from level to locally playing players)
4073
4074     for (i = 0; i < MAX_PLAYERS; i++)
4075       if (setup.input[i].use_joystick ||
4076           setup.input[i].key.left != KSYM_UNDEFINED)
4077         stored_player[i].connected = TRUE;
4078   }
4079
4080 #if DEBUG_INIT_PLAYER
4081   DebugPrintPlayerStatus("Player status after level initialization");
4082 #endif
4083
4084 #if DEBUG_INIT_PLAYER
4085   Debug("game:init:player", "Reassigning players ...");
4086 #endif
4087
4088   // check if any connected player was not found in playfield
4089   for (i = 0; i < MAX_PLAYERS; i++)
4090   {
4091     struct PlayerInfo *player = &stored_player[i];
4092
4093     if (player->connected && !player->present)
4094     {
4095       struct PlayerInfo *field_player = NULL;
4096
4097 #if DEBUG_INIT_PLAYER
4098       Debug("game:init:player",
4099             "- looking for field player for player %d ...", i + 1);
4100 #endif
4101
4102       // assign first free player found that is present in the playfield
4103
4104       // first try: look for unmapped playfield player that is not connected
4105       for (j = 0; j < MAX_PLAYERS; j++)
4106         if (field_player == NULL &&
4107             stored_player[j].present &&
4108             !stored_player[j].mapped &&
4109             !stored_player[j].connected)
4110           field_player = &stored_player[j];
4111
4112       // second try: look for *any* unmapped playfield player
4113       for (j = 0; j < MAX_PLAYERS; j++)
4114         if (field_player == NULL &&
4115             stored_player[j].present &&
4116             !stored_player[j].mapped)
4117           field_player = &stored_player[j];
4118
4119       if (field_player != NULL)
4120       {
4121         int jx = field_player->jx, jy = field_player->jy;
4122
4123 #if DEBUG_INIT_PLAYER
4124         Debug("game:init:player", "- found player %d",
4125               field_player->index_nr + 1);
4126 #endif
4127
4128         player->present = FALSE;
4129         player->active = FALSE;
4130
4131         field_player->present = TRUE;
4132         field_player->active = TRUE;
4133
4134         /*
4135         player->initial_element = field_player->initial_element;
4136         player->artwork_element = field_player->artwork_element;
4137
4138         player->block_last_field       = field_player->block_last_field;
4139         player->block_delay_adjustment = field_player->block_delay_adjustment;
4140         */
4141
4142         StorePlayer[jx][jy] = field_player->element_nr;
4143
4144         field_player->jx = field_player->last_jx = jx;
4145         field_player->jy = field_player->last_jy = jy;
4146
4147         if (local_player == player)
4148           local_player = field_player;
4149
4150         map_player_action[field_player->index_nr] = i;
4151
4152         field_player->mapped = TRUE;
4153
4154 #if DEBUG_INIT_PLAYER
4155         Debug("game:init:player", "- map_player_action[%d] == %d",
4156               field_player->index_nr + 1, i + 1);
4157 #endif
4158       }
4159     }
4160
4161     if (player->connected && player->present)
4162       player->mapped = TRUE;
4163   }
4164
4165 #if DEBUG_INIT_PLAYER
4166   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4167 #endif
4168
4169 #else
4170
4171   // check if any connected player was not found in playfield
4172   for (i = 0; i < MAX_PLAYERS; i++)
4173   {
4174     struct PlayerInfo *player = &stored_player[i];
4175
4176     if (player->connected && !player->present)
4177     {
4178       for (j = 0; j < MAX_PLAYERS; j++)
4179       {
4180         struct PlayerInfo *field_player = &stored_player[j];
4181         int jx = field_player->jx, jy = field_player->jy;
4182
4183         // assign first free player found that is present in the playfield
4184         if (field_player->present && !field_player->connected)
4185         {
4186           player->present = TRUE;
4187           player->active = TRUE;
4188
4189           field_player->present = FALSE;
4190           field_player->active = FALSE;
4191
4192           player->initial_element = field_player->initial_element;
4193           player->artwork_element = field_player->artwork_element;
4194
4195           player->block_last_field       = field_player->block_last_field;
4196           player->block_delay_adjustment = field_player->block_delay_adjustment;
4197
4198           StorePlayer[jx][jy] = player->element_nr;
4199
4200           player->jx = player->last_jx = jx;
4201           player->jy = player->last_jy = jy;
4202
4203           break;
4204         }
4205       }
4206     }
4207   }
4208 #endif
4209
4210 #if 0
4211   Debug("game:init:player", "local_player->present == %d",
4212         local_player->present);
4213 #endif
4214
4215   // set focus to local player for network games, else to all players
4216   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4217   game.centered_player_nr_next = game.centered_player_nr;
4218   game.set_centered_player = FALSE;
4219   game.set_centered_player_wrap = FALSE;
4220
4221   if (network_playing && tape.recording)
4222   {
4223     // store client dependent player focus when recording network games
4224     tape.centered_player_nr_next = game.centered_player_nr_next;
4225     tape.set_centered_player = TRUE;
4226   }
4227
4228   if (tape.playing)
4229   {
4230     // when playing a tape, eliminate all players who do not participate
4231
4232 #if USE_NEW_PLAYER_ASSIGNMENTS
4233
4234     if (!game.team_mode)
4235     {
4236       for (i = 0; i < MAX_PLAYERS; i++)
4237       {
4238         if (stored_player[i].active &&
4239             !tape.player_participates[map_player_action[i]])
4240         {
4241           struct PlayerInfo *player = &stored_player[i];
4242           int jx = player->jx, jy = player->jy;
4243
4244 #if DEBUG_INIT_PLAYER
4245           Debug("game:init:player", "Removing player %d at (%d, %d)",
4246                 i + 1, jx, jy);
4247 #endif
4248
4249           player->active = FALSE;
4250           StorePlayer[jx][jy] = 0;
4251           Tile[jx][jy] = EL_EMPTY;
4252         }
4253       }
4254     }
4255
4256 #else
4257
4258     for (i = 0; i < MAX_PLAYERS; i++)
4259     {
4260       if (stored_player[i].active &&
4261           !tape.player_participates[i])
4262       {
4263         struct PlayerInfo *player = &stored_player[i];
4264         int jx = player->jx, jy = player->jy;
4265
4266         player->active = FALSE;
4267         StorePlayer[jx][jy] = 0;
4268         Tile[jx][jy] = EL_EMPTY;
4269       }
4270     }
4271 #endif
4272   }
4273   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4274   {
4275     // when in single player mode, eliminate all but the local player
4276
4277     for (i = 0; i < MAX_PLAYERS; i++)
4278     {
4279       struct PlayerInfo *player = &stored_player[i];
4280
4281       if (player->active && player != local_player)
4282       {
4283         int jx = player->jx, jy = player->jy;
4284
4285         player->active = FALSE;
4286         player->present = FALSE;
4287
4288         StorePlayer[jx][jy] = 0;
4289         Tile[jx][jy] = EL_EMPTY;
4290       }
4291     }
4292   }
4293
4294   for (i = 0; i < MAX_PLAYERS; i++)
4295     if (stored_player[i].active)
4296       game.players_still_needed++;
4297
4298   if (level.solved_by_one_player)
4299     game.players_still_needed = 1;
4300
4301   // when recording the game, store which players take part in the game
4302   if (tape.recording)
4303   {
4304 #if USE_NEW_PLAYER_ASSIGNMENTS
4305     for (i = 0; i < MAX_PLAYERS; i++)
4306       if (stored_player[i].connected)
4307         tape.player_participates[i] = TRUE;
4308 #else
4309     for (i = 0; i < MAX_PLAYERS; i++)
4310       if (stored_player[i].active)
4311         tape.player_participates[i] = TRUE;
4312 #endif
4313   }
4314
4315 #if DEBUG_INIT_PLAYER
4316   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4317 #endif
4318
4319   if (BorderElement == EL_EMPTY)
4320   {
4321     SBX_Left = 0;
4322     SBX_Right = lev_fieldx - SCR_FIELDX;
4323     SBY_Upper = 0;
4324     SBY_Lower = lev_fieldy - SCR_FIELDY;
4325   }
4326   else
4327   {
4328     SBX_Left = -1;
4329     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4330     SBY_Upper = -1;
4331     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4332   }
4333
4334   if (full_lev_fieldx <= SCR_FIELDX)
4335     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4336   if (full_lev_fieldy <= SCR_FIELDY)
4337     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4338
4339   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4340     SBX_Left--;
4341   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4342     SBY_Upper--;
4343
4344   // if local player not found, look for custom element that might create
4345   // the player (make some assumptions about the right custom element)
4346   if (!local_player->present)
4347   {
4348     int start_x = 0, start_y = 0;
4349     int found_rating = 0;
4350     int found_element = EL_UNDEFINED;
4351     int player_nr = local_player->index_nr;
4352
4353     SCAN_PLAYFIELD(x, y)
4354     {
4355       int element = Tile[x][y];
4356       int content;
4357       int xx, yy;
4358       boolean is_player;
4359
4360       if (level.use_start_element[player_nr] &&
4361           level.start_element[player_nr] == element &&
4362           found_rating < 4)
4363       {
4364         start_x = x;
4365         start_y = y;
4366
4367         found_rating = 4;
4368         found_element = element;
4369       }
4370
4371       if (!IS_CUSTOM_ELEMENT(element))
4372         continue;
4373
4374       if (CAN_CHANGE(element))
4375       {
4376         for (i = 0; i < element_info[element].num_change_pages; i++)
4377         {
4378           // check for player created from custom element as single target
4379           content = element_info[element].change_page[i].target_element;
4380           is_player = IS_PLAYER_ELEMENT(content);
4381
4382           if (is_player && (found_rating < 3 ||
4383                             (found_rating == 3 && element < found_element)))
4384           {
4385             start_x = x;
4386             start_y = y;
4387
4388             found_rating = 3;
4389             found_element = element;
4390           }
4391         }
4392       }
4393
4394       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4395       {
4396         // check for player created from custom element as explosion content
4397         content = element_info[element].content.e[xx][yy];
4398         is_player = IS_PLAYER_ELEMENT(content);
4399
4400         if (is_player && (found_rating < 2 ||
4401                           (found_rating == 2 && element < found_element)))
4402         {
4403           start_x = x + xx - 1;
4404           start_y = y + yy - 1;
4405
4406           found_rating = 2;
4407           found_element = element;
4408         }
4409
4410         if (!CAN_CHANGE(element))
4411           continue;
4412
4413         for (i = 0; i < element_info[element].num_change_pages; i++)
4414         {
4415           // check for player created from custom element as extended target
4416           content =
4417             element_info[element].change_page[i].target_content.e[xx][yy];
4418
4419           is_player = IS_PLAYER_ELEMENT(content);
4420
4421           if (is_player && (found_rating < 1 ||
4422                             (found_rating == 1 && element < found_element)))
4423           {
4424             start_x = x + xx - 1;
4425             start_y = y + yy - 1;
4426
4427             found_rating = 1;
4428             found_element = element;
4429           }
4430         }
4431       }
4432     }
4433
4434     scroll_x = SCROLL_POSITION_X(start_x);
4435     scroll_y = SCROLL_POSITION_Y(start_y);
4436   }
4437   else
4438   {
4439     scroll_x = SCROLL_POSITION_X(local_player->jx);
4440     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4441   }
4442
4443   // !!! FIX THIS (START) !!!
4444   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4445   {
4446     InitGameEngine_EM();
4447   }
4448   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4449   {
4450     InitGameEngine_SP();
4451   }
4452   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4453   {
4454     InitGameEngine_MM();
4455   }
4456   else
4457   {
4458     DrawLevel(REDRAW_FIELD);
4459     DrawAllPlayers();
4460
4461     // after drawing the level, correct some elements
4462     if (game.timegate_time_left == 0)
4463       CloseAllOpenTimegates();
4464   }
4465
4466   // blit playfield from scroll buffer to normal back buffer for fading in
4467   BlitScreenToBitmap(backbuffer);
4468   // !!! FIX THIS (END) !!!
4469
4470   DrawMaskedBorder(fade_mask);
4471
4472   FadeIn(fade_mask);
4473
4474 #if 1
4475   // full screen redraw is required at this point in the following cases:
4476   // - special editor door undrawn when game was started from level editor
4477   // - drawing area (playfield) was changed and has to be removed completely
4478   redraw_mask = REDRAW_ALL;
4479   BackToFront();
4480 #endif
4481
4482   if (!game.restart_level)
4483   {
4484     // copy default game door content to main double buffer
4485
4486     // !!! CHECK AGAIN !!!
4487     SetPanelBackground();
4488     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4489     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4490   }
4491
4492   SetPanelBackground();
4493   SetDrawBackgroundMask(REDRAW_DOOR_1);
4494
4495   UpdateAndDisplayGameControlValues();
4496
4497   if (!game.restart_level)
4498   {
4499     UnmapGameButtons();
4500     UnmapTapeButtons();
4501
4502     FreeGameButtons();
4503     CreateGameButtons();
4504
4505     MapGameButtons();
4506     MapTapeButtons();
4507
4508     // copy actual game door content to door double buffer for OpenDoor()
4509     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4510
4511     OpenDoor(DOOR_OPEN_ALL);
4512
4513     KeyboardAutoRepeatOffUnlessAutoplay();
4514
4515 #if DEBUG_INIT_PLAYER
4516     DebugPrintPlayerStatus("Player status (final)");
4517 #endif
4518   }
4519
4520   UnmapAllGadgets();
4521
4522   MapGameButtons();
4523   MapTapeButtons();
4524
4525   if (!game.restart_level && !tape.playing)
4526   {
4527     LevelStats_incPlayed(level_nr);
4528
4529     SaveLevelSetup_SeriesInfo();
4530   }
4531
4532   game.restart_level = FALSE;
4533
4534   game.request_active = FALSE;
4535   game.request_active_or_moving = FALSE;
4536
4537   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4538     InitGameActions_MM();
4539
4540   SaveEngineSnapshotToListInitial();
4541
4542   if (!game.restart_level)
4543   {
4544     PlaySound(SND_GAME_STARTING);
4545
4546     if (setup.sound_music)
4547       PlayLevelMusic();
4548   }
4549
4550   SetPlayfieldMouseCursorEnabled(!game.use_mouse_actions);
4551 }
4552
4553 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4554                         int actual_player_x, int actual_player_y)
4555 {
4556   // this is used for non-R'n'D game engines to update certain engine values
4557
4558   // needed to determine if sounds are played within the visible screen area
4559   scroll_x = actual_scroll_x;
4560   scroll_y = actual_scroll_y;
4561
4562   // needed to get player position for "follow finger" playing input method
4563   local_player->jx = actual_player_x;
4564   local_player->jy = actual_player_y;
4565 }
4566
4567 void InitMovDir(int x, int y)
4568 {
4569   int i, element = Tile[x][y];
4570   static int xy[4][2] =
4571   {
4572     {  0, +1 },
4573     { +1,  0 },
4574     {  0, -1 },
4575     { -1,  0 }
4576   };
4577   static int direction[3][4] =
4578   {
4579     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4580     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4581     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4582   };
4583
4584   switch (element)
4585   {
4586     case EL_BUG_RIGHT:
4587     case EL_BUG_UP:
4588     case EL_BUG_LEFT:
4589     case EL_BUG_DOWN:
4590       Tile[x][y] = EL_BUG;
4591       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4592       break;
4593
4594     case EL_SPACESHIP_RIGHT:
4595     case EL_SPACESHIP_UP:
4596     case EL_SPACESHIP_LEFT:
4597     case EL_SPACESHIP_DOWN:
4598       Tile[x][y] = EL_SPACESHIP;
4599       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4600       break;
4601
4602     case EL_BD_BUTTERFLY_RIGHT:
4603     case EL_BD_BUTTERFLY_UP:
4604     case EL_BD_BUTTERFLY_LEFT:
4605     case EL_BD_BUTTERFLY_DOWN:
4606       Tile[x][y] = EL_BD_BUTTERFLY;
4607       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4608       break;
4609
4610     case EL_BD_FIREFLY_RIGHT:
4611     case EL_BD_FIREFLY_UP:
4612     case EL_BD_FIREFLY_LEFT:
4613     case EL_BD_FIREFLY_DOWN:
4614       Tile[x][y] = EL_BD_FIREFLY;
4615       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4616       break;
4617
4618     case EL_PACMAN_RIGHT:
4619     case EL_PACMAN_UP:
4620     case EL_PACMAN_LEFT:
4621     case EL_PACMAN_DOWN:
4622       Tile[x][y] = EL_PACMAN;
4623       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4624       break;
4625
4626     case EL_YAMYAM_LEFT:
4627     case EL_YAMYAM_RIGHT:
4628     case EL_YAMYAM_UP:
4629     case EL_YAMYAM_DOWN:
4630       Tile[x][y] = EL_YAMYAM;
4631       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4632       break;
4633
4634     case EL_SP_SNIKSNAK:
4635       MovDir[x][y] = MV_UP;
4636       break;
4637
4638     case EL_SP_ELECTRON:
4639       MovDir[x][y] = MV_LEFT;
4640       break;
4641
4642     case EL_MOLE_LEFT:
4643     case EL_MOLE_RIGHT:
4644     case EL_MOLE_UP:
4645     case EL_MOLE_DOWN:
4646       Tile[x][y] = EL_MOLE;
4647       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4648       break;
4649
4650     case EL_SPRING_LEFT:
4651     case EL_SPRING_RIGHT:
4652       Tile[x][y] = EL_SPRING;
4653       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4654       break;
4655
4656     default:
4657       if (IS_CUSTOM_ELEMENT(element))
4658       {
4659         struct ElementInfo *ei = &element_info[element];
4660         int move_direction_initial = ei->move_direction_initial;
4661         int move_pattern = ei->move_pattern;
4662
4663         if (move_direction_initial == MV_START_PREVIOUS)
4664         {
4665           if (MovDir[x][y] != MV_NONE)
4666             return;
4667
4668           move_direction_initial = MV_START_AUTOMATIC;
4669         }
4670
4671         if (move_direction_initial == MV_START_RANDOM)
4672           MovDir[x][y] = 1 << RND(4);
4673         else if (move_direction_initial & MV_ANY_DIRECTION)
4674           MovDir[x][y] = move_direction_initial;
4675         else if (move_pattern == MV_ALL_DIRECTIONS ||
4676                  move_pattern == MV_TURNING_LEFT ||
4677                  move_pattern == MV_TURNING_RIGHT ||
4678                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4679                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4680                  move_pattern == MV_TURNING_RANDOM)
4681           MovDir[x][y] = 1 << RND(4);
4682         else if (move_pattern == MV_HORIZONTAL)
4683           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4684         else if (move_pattern == MV_VERTICAL)
4685           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4686         else if (move_pattern & MV_ANY_DIRECTION)
4687           MovDir[x][y] = element_info[element].move_pattern;
4688         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4689                  move_pattern == MV_ALONG_RIGHT_SIDE)
4690         {
4691           // use random direction as default start direction
4692           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4693             MovDir[x][y] = 1 << RND(4);
4694
4695           for (i = 0; i < NUM_DIRECTIONS; i++)
4696           {
4697             int x1 = x + xy[i][0];
4698             int y1 = y + xy[i][1];
4699
4700             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4701             {
4702               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4703                 MovDir[x][y] = direction[0][i];
4704               else
4705                 MovDir[x][y] = direction[1][i];
4706
4707               break;
4708             }
4709           }
4710         }                
4711       }
4712       else
4713       {
4714         MovDir[x][y] = 1 << RND(4);
4715
4716         if (element != EL_BUG &&
4717             element != EL_SPACESHIP &&
4718             element != EL_BD_BUTTERFLY &&
4719             element != EL_BD_FIREFLY)
4720           break;
4721
4722         for (i = 0; i < NUM_DIRECTIONS; i++)
4723         {
4724           int x1 = x + xy[i][0];
4725           int y1 = y + xy[i][1];
4726
4727           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4728           {
4729             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4730             {
4731               MovDir[x][y] = direction[0][i];
4732               break;
4733             }
4734             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4735                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4736             {
4737               MovDir[x][y] = direction[1][i];
4738               break;
4739             }
4740           }
4741         }
4742       }
4743       break;
4744   }
4745
4746   GfxDir[x][y] = MovDir[x][y];
4747 }
4748
4749 void InitAmoebaNr(int x, int y)
4750 {
4751   int i;
4752   int group_nr = AmoebaNeighbourNr(x, y);
4753
4754   if (group_nr == 0)
4755   {
4756     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4757     {
4758       if (AmoebaCnt[i] == 0)
4759       {
4760         group_nr = i;
4761         break;
4762       }
4763     }
4764   }
4765
4766   AmoebaNr[x][y] = group_nr;
4767   AmoebaCnt[group_nr]++;
4768   AmoebaCnt2[group_nr]++;
4769 }
4770
4771 static void LevelSolved_SetFinalGameValues(void)
4772 {
4773   game.time_final = (game.no_level_time_limit ? TimePlayed : TimeLeft);
4774   game.score_time_final = (level.use_step_counter ? TimePlayed :
4775                            TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4776
4777   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4778                       game_em.lev->score :
4779                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4780                       game_mm.score :
4781                       game.score);
4782
4783   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4784                        MM_HEALTH(game_mm.laser_overload_value) :
4785                        game.health);
4786
4787   game.LevelSolved_CountingTime = game.time_final;
4788   game.LevelSolved_CountingScore = game.score_final;
4789   game.LevelSolved_CountingHealth = game.health_final;
4790 }
4791
4792 static void LevelSolved_DisplayFinalGameValues(int time, int score, int health)
4793 {
4794   game.LevelSolved_CountingTime = time;
4795   game.LevelSolved_CountingScore = score;
4796   game.LevelSolved_CountingHealth = health;
4797
4798   game_panel_controls[GAME_PANEL_TIME].value = time;
4799   game_panel_controls[GAME_PANEL_SCORE].value = score;
4800   game_panel_controls[GAME_PANEL_HEALTH].value = health;
4801
4802   DisplayGameControlValues();
4803 }
4804
4805 static void LevelSolved(void)
4806 {
4807   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4808       game.players_still_needed > 0)
4809     return;
4810
4811   game.LevelSolved = TRUE;
4812   game.GameOver = TRUE;
4813
4814   tape.solved = TRUE;
4815
4816   // needed here to display correct panel values while player walks into exit
4817   LevelSolved_SetFinalGameValues();
4818 }
4819
4820 void GameWon(void)
4821 {
4822   static int time_count_steps;
4823   static int time, time_final;
4824   static float score, score_final; // needed for time score < 10 for 10 seconds
4825   static int health, health_final;
4826   static int game_over_delay_1 = 0;
4827   static int game_over_delay_2 = 0;
4828   static int game_over_delay_3 = 0;
4829   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4830   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4831
4832   if (!game.LevelSolved_GameWon)
4833   {
4834     int i;
4835
4836     // do not start end game actions before the player stops moving (to exit)
4837     if (local_player->active && local_player->MovPos)
4838       return;
4839
4840     // calculate final game values after player finished walking into exit
4841     LevelSolved_SetFinalGameValues();
4842
4843     game.LevelSolved_GameWon = TRUE;
4844     game.LevelSolved_SaveTape = tape.recording;
4845     game.LevelSolved_SaveScore = !tape.playing;
4846
4847     if (!tape.playing)
4848     {
4849       LevelStats_incSolved(level_nr);
4850
4851       SaveLevelSetup_SeriesInfo();
4852     }
4853
4854     if (tape.auto_play)         // tape might already be stopped here
4855       tape.auto_play_level_solved = TRUE;
4856
4857     TapeStop();
4858
4859     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4860     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4861     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4862
4863     time = time_final = game.time_final;
4864     score = score_final = game.score_final;
4865     health = health_final = game.health_final;
4866
4867     // update game panel values before (delayed) counting of score (if any)
4868     LevelSolved_DisplayFinalGameValues(time, score, health);
4869
4870     // if level has time score defined, calculate new final game values
4871     if (time_score > 0)
4872     {
4873       int time_final_max = 999;
4874       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4875       int time_frames = 0;
4876       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4877       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4878
4879       if (TimeLeft > 0)
4880       {
4881         time_final = 0;
4882         time_frames = time_frames_left;
4883       }
4884       else if (game.no_level_time_limit && TimePlayed < time_final_max)
4885       {
4886         time_final = time_final_max;
4887         time_frames = time_frames_final_max - time_frames_played;
4888       }
4889
4890       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4891
4892       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4893
4894       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4895       {
4896         health_final = 0;
4897         score_final += health * time_score;
4898       }
4899
4900       game.score_final = score_final;
4901       game.health_final = health_final;
4902     }
4903
4904     // if not counting score after game, immediately update game panel values
4905     if (level_editor_test_game || !setup.count_score_after_game)
4906     {
4907       time = time_final;
4908       score = score_final;
4909
4910       LevelSolved_DisplayFinalGameValues(time, score, health);
4911     }
4912
4913     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4914     {
4915       // check if last player has left the level
4916       if (game.exit_x >= 0 &&
4917           game.exit_y >= 0)
4918       {
4919         int x = game.exit_x;
4920         int y = game.exit_y;
4921         int element = Tile[x][y];
4922
4923         // close exit door after last player
4924         if ((game.all_players_gone &&
4925              (element == EL_EXIT_OPEN ||
4926               element == EL_SP_EXIT_OPEN ||
4927               element == EL_STEEL_EXIT_OPEN)) ||
4928             element == EL_EM_EXIT_OPEN ||
4929             element == EL_EM_STEEL_EXIT_OPEN)
4930         {
4931
4932           Tile[x][y] =
4933             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4934              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4935              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4936              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4937              EL_EM_STEEL_EXIT_CLOSING);
4938
4939           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4940         }
4941
4942         // player disappears
4943         DrawLevelField(x, y);
4944       }
4945
4946       for (i = 0; i < MAX_PLAYERS; i++)
4947       {
4948         struct PlayerInfo *player = &stored_player[i];
4949
4950         if (player->present)
4951         {
4952           RemovePlayer(player);
4953
4954           // player disappears
4955           DrawLevelField(player->jx, player->jy);
4956         }
4957       }
4958     }
4959
4960     PlaySound(SND_GAME_WINNING);
4961   }
4962
4963   if (setup.count_score_after_game)
4964   {
4965     if (time != time_final)
4966     {
4967       if (game_over_delay_1 > 0)
4968       {
4969         game_over_delay_1--;
4970
4971         return;
4972       }
4973
4974       int time_to_go = ABS(time_final - time);
4975       int time_count_dir = (time < time_final ? +1 : -1);
4976
4977       if (time_to_go < time_count_steps)
4978         time_count_steps = 1;
4979
4980       time  += time_count_steps * time_count_dir;
4981       score += time_count_steps * time_score;
4982
4983       // set final score to correct rounding differences after counting score
4984       if (time == time_final)
4985         score = score_final;
4986
4987       LevelSolved_DisplayFinalGameValues(time, score, health);
4988
4989       if (time == time_final)
4990         StopSound(SND_GAME_LEVELTIME_BONUS);
4991       else if (setup.sound_loops)
4992         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4993       else
4994         PlaySound(SND_GAME_LEVELTIME_BONUS);
4995
4996       return;
4997     }
4998
4999     if (health != health_final)
5000     {
5001       if (game_over_delay_2 > 0)
5002       {
5003         game_over_delay_2--;
5004
5005         return;
5006       }
5007
5008       int health_count_dir = (health < health_final ? +1 : -1);
5009
5010       health += health_count_dir;
5011       score  += time_score;
5012
5013       LevelSolved_DisplayFinalGameValues(time, score, health);
5014
5015       if (health == health_final)
5016         StopSound(SND_GAME_LEVELTIME_BONUS);
5017       else if (setup.sound_loops)
5018         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5019       else
5020         PlaySound(SND_GAME_LEVELTIME_BONUS);
5021
5022       return;
5023     }
5024   }
5025
5026   game.panel.active = FALSE;
5027
5028   if (game_over_delay_3 > 0)
5029   {
5030     game_over_delay_3--;
5031
5032     return;
5033   }
5034
5035   GameEnd();
5036 }
5037
5038 void GameEnd(void)
5039 {
5040   // used instead of "level_nr" (needed for network games)
5041   int last_level_nr = levelset.level_nr;
5042   boolean tape_saved = FALSE;
5043
5044   game.LevelSolved_GameEnd = TRUE;
5045
5046   if (game.LevelSolved_SaveTape && !score_info_tape_play)
5047   {
5048     // make sure that request dialog to save tape does not open door again
5049     if (!global.use_envelope_request)
5050       CloseDoor(DOOR_CLOSE_1);
5051
5052     // ask to save tape
5053     tape_saved = SaveTapeChecked_LevelSolved(tape.level_nr);
5054
5055     // set unique basename for score tape (also saved in high score table)
5056     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
5057   }
5058
5059   // if no tape is to be saved, close both doors simultaneously
5060   CloseDoor(DOOR_CLOSE_ALL);
5061
5062   if (level_editor_test_game || score_info_tape_play)
5063   {
5064     SetGameStatus(GAME_MODE_MAIN);
5065
5066     DrawMainMenu();
5067
5068     return;
5069   }
5070
5071   if (!game.LevelSolved_SaveScore)
5072   {
5073     SetGameStatus(GAME_MODE_MAIN);
5074
5075     DrawMainMenu();
5076
5077     return;
5078   }
5079
5080   if (level_nr == leveldir_current->handicap_level)
5081   {
5082     leveldir_current->handicap_level++;
5083
5084     SaveLevelSetup_SeriesInfo();
5085   }
5086
5087   // save score and score tape before potentially erasing tape below
5088   NewHighScore(last_level_nr, tape_saved);
5089
5090   if (setup.increment_levels &&
5091       level_nr < leveldir_current->last_level &&
5092       !network_playing)
5093   {
5094     level_nr++;         // advance to next level
5095     TapeErase();        // start with empty tape
5096
5097     if (setup.auto_play_next_level)
5098     {
5099       scores.continue_playing = TRUE;
5100       scores.next_level_nr = level_nr;
5101
5102       LoadLevel(level_nr);
5103
5104       SaveLevelSetup_SeriesInfo();
5105     }
5106   }
5107
5108   if (scores.last_added >= 0 && setup.show_scores_after_game)
5109   {
5110     SetGameStatus(GAME_MODE_SCORES);
5111
5112     DrawHallOfFame(last_level_nr);
5113   }
5114   else if (scores.continue_playing)
5115   {
5116     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5117   }
5118   else
5119   {
5120     SetGameStatus(GAME_MODE_MAIN);
5121
5122     DrawMainMenu();
5123   }
5124 }
5125
5126 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5127                          boolean one_score_entry_per_name)
5128 {
5129   int i;
5130
5131   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5132     return -1;
5133
5134   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5135   {
5136     struct ScoreEntry *entry = &list->entry[i];
5137     boolean score_is_better = (new_entry->score >  entry->score);
5138     boolean score_is_equal  = (new_entry->score == entry->score);
5139     boolean time_is_better  = (new_entry->time  <  entry->time);
5140     boolean time_is_equal   = (new_entry->time  == entry->time);
5141     boolean better_by_score = (score_is_better ||
5142                                (score_is_equal && time_is_better));
5143     boolean better_by_time  = (time_is_better ||
5144                                (time_is_equal && score_is_better));
5145     boolean is_better = (level.rate_time_over_score ? better_by_time :
5146                          better_by_score);
5147     boolean entry_is_empty = (entry->score == 0 &&
5148                               entry->time == 0);
5149
5150     // prevent adding server score entries if also existing in local score file
5151     // (special case: historic score entries have an empty tape basename entry)
5152     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5153         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5154     {
5155       // add fields from server score entry not stored in local score entry
5156       // (currently, this means setting platform, version and country fields;
5157       // in rare cases, this may also correct an invalid score value, as
5158       // historic scores might have been truncated to 16-bit values locally)
5159       *entry = *new_entry;
5160
5161       return -1;
5162     }
5163
5164     if (is_better || entry_is_empty)
5165     {
5166       // player has made it to the hall of fame
5167
5168       if (i < MAX_SCORE_ENTRIES - 1)
5169       {
5170         int m = MAX_SCORE_ENTRIES - 1;
5171         int l;
5172
5173         if (one_score_entry_per_name)
5174         {
5175           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5176             if (strEqual(list->entry[l].name, new_entry->name))
5177               m = l;
5178
5179           if (m == i)   // player's new highscore overwrites his old one
5180             goto put_into_list;
5181         }
5182
5183         for (l = m; l > i; l--)
5184           list->entry[l] = list->entry[l - 1];
5185       }
5186
5187       put_into_list:
5188
5189       *entry = *new_entry;
5190
5191       return i;
5192     }
5193     else if (one_score_entry_per_name &&
5194              strEqual(entry->name, new_entry->name))
5195     {
5196       // player already in high score list with better score or time
5197
5198       return -1;
5199     }
5200   }
5201
5202   // special case: new score is beyond the last high score list position
5203   return MAX_SCORE_ENTRIES;
5204 }
5205
5206 void NewHighScore(int level_nr, boolean tape_saved)
5207 {
5208   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5209   boolean one_per_name = FALSE;
5210
5211   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5212   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5213
5214   new_entry.score = game.score_final;
5215   new_entry.time = game.score_time_final;
5216
5217   LoadScore(level_nr);
5218
5219   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5220
5221   if (scores.last_added >= MAX_SCORE_ENTRIES)
5222   {
5223     scores.last_added = MAX_SCORE_ENTRIES - 1;
5224     scores.force_last_added = TRUE;
5225
5226     scores.entry[scores.last_added] = new_entry;
5227
5228     // store last added local score entry (before merging server scores)
5229     scores.last_added_local = scores.last_added;
5230
5231     return;
5232   }
5233
5234   if (scores.last_added < 0)
5235     return;
5236
5237   SaveScore(level_nr);
5238
5239   // store last added local score entry (before merging server scores)
5240   scores.last_added_local = scores.last_added;
5241
5242   if (!game.LevelSolved_SaveTape)
5243     return;
5244
5245   SaveScoreTape(level_nr);
5246
5247   if (setup.ask_for_using_api_server)
5248   {
5249     setup.use_api_server =
5250       Request("Upload your score and tape to the high score server?", REQ_ASK);
5251
5252     if (!setup.use_api_server)
5253       Request("Not using high score server! Use setup menu to enable again!",
5254               REQ_CONFIRM);
5255
5256     runtime.use_api_server = setup.use_api_server;
5257
5258     // after asking for using API server once, do not ask again
5259     setup.ask_for_using_api_server = FALSE;
5260
5261     SaveSetup_ServerSetup();
5262   }
5263
5264   SaveServerScore(level_nr, tape_saved);
5265 }
5266
5267 void MergeServerScore(void)
5268 {
5269   struct ScoreEntry last_added_entry;
5270   boolean one_per_name = FALSE;
5271   int i;
5272
5273   if (scores.last_added >= 0)
5274     last_added_entry = scores.entry[scores.last_added];
5275
5276   for (i = 0; i < server_scores.num_entries; i++)
5277   {
5278     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5279
5280     if (pos >= 0 && pos <= scores.last_added)
5281       scores.last_added++;
5282   }
5283
5284   if (scores.last_added >= MAX_SCORE_ENTRIES)
5285   {
5286     scores.last_added = MAX_SCORE_ENTRIES - 1;
5287     scores.force_last_added = TRUE;
5288
5289     scores.entry[scores.last_added] = last_added_entry;
5290   }
5291 }
5292
5293 static int getElementMoveStepsizeExt(int x, int y, int direction)
5294 {
5295   int element = Tile[x][y];
5296   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5297   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5298   int horiz_move = (dx != 0);
5299   int sign = (horiz_move ? dx : dy);
5300   int step = sign * element_info[element].move_stepsize;
5301
5302   // special values for move stepsize for spring and things on conveyor belt
5303   if (horiz_move)
5304   {
5305     if (CAN_FALL(element) &&
5306         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5307       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5308     else if (element == EL_SPRING)
5309       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5310   }
5311
5312   return step;
5313 }
5314
5315 static int getElementMoveStepsize(int x, int y)
5316 {
5317   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5318 }
5319
5320 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5321 {
5322   if (player->GfxAction != action || player->GfxDir != dir)
5323   {
5324     player->GfxAction = action;
5325     player->GfxDir = dir;
5326     player->Frame = 0;
5327     player->StepFrame = 0;
5328   }
5329 }
5330
5331 static void ResetGfxFrame(int x, int y)
5332 {
5333   // profiling showed that "autotest" spends 10~20% of its time in this function
5334   if (DrawingDeactivatedField())
5335     return;
5336
5337   int element = Tile[x][y];
5338   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5339
5340   if (graphic_info[graphic].anim_global_sync)
5341     GfxFrame[x][y] = FrameCounter;
5342   else if (graphic_info[graphic].anim_global_anim_sync)
5343     GfxFrame[x][y] = getGlobalAnimSyncFrame();
5344   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5345     GfxFrame[x][y] = CustomValue[x][y];
5346   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5347     GfxFrame[x][y] = element_info[element].collect_score;
5348   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5349     GfxFrame[x][y] = ChangeDelay[x][y];
5350 }
5351
5352 static void ResetGfxAnimation(int x, int y)
5353 {
5354   GfxAction[x][y] = ACTION_DEFAULT;
5355   GfxDir[x][y] = MovDir[x][y];
5356   GfxFrame[x][y] = 0;
5357
5358   ResetGfxFrame(x, y);
5359 }
5360
5361 static void ResetRandomAnimationValue(int x, int y)
5362 {
5363   GfxRandom[x][y] = INIT_GFX_RANDOM();
5364 }
5365
5366 static void InitMovingField(int x, int y, int direction)
5367 {
5368   int element = Tile[x][y];
5369   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5370   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5371   int newx = x + dx;
5372   int newy = y + dy;
5373   boolean is_moving_before, is_moving_after;
5374
5375   // check if element was/is moving or being moved before/after mode change
5376   is_moving_before = (WasJustMoving[x][y] != 0);
5377   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5378
5379   // reset animation only for moving elements which change direction of moving
5380   // or which just started or stopped moving
5381   // (else CEs with property "can move" / "not moving" are reset each frame)
5382   if (is_moving_before != is_moving_after ||
5383       direction != MovDir[x][y])
5384     ResetGfxAnimation(x, y);
5385
5386   MovDir[x][y] = direction;
5387   GfxDir[x][y] = direction;
5388
5389   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5390                      direction == MV_DOWN && CAN_FALL(element) ?
5391                      ACTION_FALLING : ACTION_MOVING);
5392
5393   // this is needed for CEs with property "can move" / "not moving"
5394
5395   if (is_moving_after)
5396   {
5397     if (Tile[newx][newy] == EL_EMPTY)
5398       Tile[newx][newy] = EL_BLOCKED;
5399
5400     MovDir[newx][newy] = MovDir[x][y];
5401
5402     CustomValue[newx][newy] = CustomValue[x][y];
5403
5404     GfxFrame[newx][newy] = GfxFrame[x][y];
5405     GfxRandom[newx][newy] = GfxRandom[x][y];
5406     GfxAction[newx][newy] = GfxAction[x][y];
5407     GfxDir[newx][newy] = GfxDir[x][y];
5408   }
5409 }
5410
5411 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5412 {
5413   int direction = MovDir[x][y];
5414   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5415   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5416
5417   *goes_to_x = newx;
5418   *goes_to_y = newy;
5419 }
5420
5421 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5422 {
5423   int direction = MovDir[x][y];
5424   int oldx = x + (direction & MV_LEFT ? +1 : direction & MV_RIGHT ? -1 : 0);
5425   int oldy = y + (direction & MV_UP   ? +1 : direction & MV_DOWN  ? -1 : 0);
5426
5427   *comes_from_x = oldx;
5428   *comes_from_y = oldy;
5429 }
5430
5431 static int MovingOrBlocked2Element(int x, int y)
5432 {
5433   int element = Tile[x][y];
5434
5435   if (element == EL_BLOCKED)
5436   {
5437     int oldx, oldy;
5438
5439     Blocked2Moving(x, y, &oldx, &oldy);
5440
5441     return Tile[oldx][oldy];
5442   }
5443
5444   return element;
5445 }
5446
5447 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5448 {
5449   // like MovingOrBlocked2Element(), but if element is moving
5450   // and (x, y) is the field the moving element is just leaving,
5451   // return EL_BLOCKED instead of the element value
5452   int element = Tile[x][y];
5453
5454   if (IS_MOVING(x, y))
5455   {
5456     if (element == EL_BLOCKED)
5457     {
5458       int oldx, oldy;
5459
5460       Blocked2Moving(x, y, &oldx, &oldy);
5461       return Tile[oldx][oldy];
5462     }
5463     else
5464       return EL_BLOCKED;
5465   }
5466   else
5467     return element;
5468 }
5469
5470 static void RemoveField(int x, int y)
5471 {
5472   Tile[x][y] = EL_EMPTY;
5473
5474   MovPos[x][y] = 0;
5475   MovDir[x][y] = 0;
5476   MovDelay[x][y] = 0;
5477
5478   CustomValue[x][y] = 0;
5479
5480   AmoebaNr[x][y] = 0;
5481   ChangeDelay[x][y] = 0;
5482   ChangePage[x][y] = -1;
5483   Pushed[x][y] = FALSE;
5484
5485   GfxElement[x][y] = EL_UNDEFINED;
5486   GfxAction[x][y] = ACTION_DEFAULT;
5487   GfxDir[x][y] = MV_NONE;
5488 }
5489
5490 static void RemoveMovingField(int x, int y)
5491 {
5492   int oldx = x, oldy = y, newx = x, newy = y;
5493   int element = Tile[x][y];
5494   int next_element = EL_UNDEFINED;
5495
5496   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5497     return;
5498
5499   if (IS_MOVING(x, y))
5500   {
5501     Moving2Blocked(x, y, &newx, &newy);
5502
5503     if (Tile[newx][newy] != EL_BLOCKED)
5504     {
5505       // element is moving, but target field is not free (blocked), but
5506       // already occupied by something different (example: acid pool);
5507       // in this case, only remove the moving field, but not the target
5508
5509       RemoveField(oldx, oldy);
5510
5511       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5512
5513       TEST_DrawLevelField(oldx, oldy);
5514
5515       return;
5516     }
5517   }
5518   else if (element == EL_BLOCKED)
5519   {
5520     Blocked2Moving(x, y, &oldx, &oldy);
5521     if (!IS_MOVING(oldx, oldy))
5522       return;
5523   }
5524
5525   if (element == EL_BLOCKED &&
5526       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5527        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5528        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5529        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5530        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5531        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5532     next_element = get_next_element(Tile[oldx][oldy]);
5533
5534   RemoveField(oldx, oldy);
5535   RemoveField(newx, newy);
5536
5537   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5538
5539   if (next_element != EL_UNDEFINED)
5540     Tile[oldx][oldy] = next_element;
5541
5542   TEST_DrawLevelField(oldx, oldy);
5543   TEST_DrawLevelField(newx, newy);
5544 }
5545
5546 void DrawDynamite(int x, int y)
5547 {
5548   int sx = SCREENX(x), sy = SCREENY(y);
5549   int graphic = el2img(Tile[x][y]);
5550   int frame;
5551
5552   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5553     return;
5554
5555   if (IS_WALKABLE_INSIDE(Back[x][y]))
5556     return;
5557
5558   if (Back[x][y])
5559     DrawLevelElement(x, y, Back[x][y]);
5560   else if (Store[x][y])
5561     DrawLevelElement(x, y, Store[x][y]);
5562   else if (game.use_masked_elements)
5563     DrawLevelElement(x, y, EL_EMPTY);
5564
5565   frame = getGraphicAnimationFrameXY(graphic, x, y);
5566
5567   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5568     DrawGraphicThruMask(sx, sy, graphic, frame);
5569   else
5570     DrawGraphic(sx, sy, graphic, frame);
5571 }
5572
5573 static void CheckDynamite(int x, int y)
5574 {
5575   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5576   {
5577     MovDelay[x][y]--;
5578
5579     if (MovDelay[x][y] != 0)
5580     {
5581       DrawDynamite(x, y);
5582       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5583
5584       return;
5585     }
5586   }
5587
5588   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5589
5590   Bang(x, y);
5591 }
5592
5593 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5594 {
5595   boolean num_checked_players = 0;
5596   int i;
5597
5598   for (i = 0; i < MAX_PLAYERS; i++)
5599   {
5600     if (stored_player[i].active)
5601     {
5602       int sx = stored_player[i].jx;
5603       int sy = stored_player[i].jy;
5604
5605       if (num_checked_players == 0)
5606       {
5607         *sx1 = *sx2 = sx;
5608         *sy1 = *sy2 = sy;
5609       }
5610       else
5611       {
5612         *sx1 = MIN(*sx1, sx);
5613         *sy1 = MIN(*sy1, sy);
5614         *sx2 = MAX(*sx2, sx);
5615         *sy2 = MAX(*sy2, sy);
5616       }
5617
5618       num_checked_players++;
5619     }
5620   }
5621 }
5622
5623 static boolean checkIfAllPlayersFitToScreen_RND(void)
5624 {
5625   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5626
5627   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5628
5629   return (sx2 - sx1 < SCR_FIELDX &&
5630           sy2 - sy1 < SCR_FIELDY);
5631 }
5632
5633 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5634 {
5635   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5636
5637   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5638
5639   *sx = (sx1 + sx2) / 2;
5640   *sy = (sy1 + sy2) / 2;
5641 }
5642
5643 static void DrawRelocateScreen(int old_x, int old_y, int x, int y,
5644                                boolean center_screen, boolean quick_relocation)
5645 {
5646   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5647   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5648   boolean no_delay = (tape.warp_forward);
5649   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5650   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5651   int new_scroll_x, new_scroll_y;
5652
5653   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5654   {
5655     // case 1: quick relocation inside visible screen (without scrolling)
5656
5657     RedrawPlayfield();
5658
5659     return;
5660   }
5661
5662   if (!level.shifted_relocation || center_screen)
5663   {
5664     // relocation _with_ centering of screen
5665
5666     new_scroll_x = SCROLL_POSITION_X(x);
5667     new_scroll_y = SCROLL_POSITION_Y(y);
5668   }
5669   else
5670   {
5671     // relocation _without_ centering of screen
5672
5673     int center_scroll_x = SCROLL_POSITION_X(old_x);
5674     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5675     int offset_x = x + (scroll_x - center_scroll_x);
5676     int offset_y = y + (scroll_y - center_scroll_y);
5677
5678     // for new screen position, apply previous offset to center position
5679     new_scroll_x = SCROLL_POSITION_X(offset_x);
5680     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5681   }
5682
5683   if (quick_relocation)
5684   {
5685     // case 2: quick relocation (redraw without visible scrolling)
5686
5687     scroll_x = new_scroll_x;
5688     scroll_y = new_scroll_y;
5689
5690     RedrawPlayfield();
5691
5692     return;
5693   }
5694
5695   // case 3: visible relocation (with scrolling to new position)
5696
5697   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5698
5699   SetVideoFrameDelay(wait_delay_value);
5700
5701   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5702   {
5703     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5704     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5705
5706     if (dx == 0 && dy == 0)             // no scrolling needed at all
5707       break;
5708
5709     scroll_x -= dx;
5710     scroll_y -= dy;
5711
5712     // set values for horizontal/vertical screen scrolling (half tile size)
5713     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5714     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5715     int pos_x = dx * TILEX / 2;
5716     int pos_y = dy * TILEY / 2;
5717     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5718     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5719
5720     ScrollLevel(dx, dy);
5721     DrawAllPlayers();
5722
5723     // scroll in two steps of half tile size to make things smoother
5724     BlitScreenToBitmapExt_RND(window, fx, fy);
5725
5726     // scroll second step to align at full tile size
5727     BlitScreenToBitmap(window);
5728   }
5729
5730   DrawAllPlayers();
5731   BackToFront();
5732
5733   SetVideoFrameDelay(frame_delay_value_old);
5734 }
5735
5736 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5737 {
5738   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5739   int player_nr = GET_PLAYER_NR(el_player);
5740   struct PlayerInfo *player = &stored_player[player_nr];
5741   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5742   boolean no_delay = (tape.warp_forward);
5743   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5744   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5745   int old_jx = player->jx;
5746   int old_jy = player->jy;
5747   int old_element = Tile[old_jx][old_jy];
5748   int element = Tile[jx][jy];
5749   boolean player_relocated = (old_jx != jx || old_jy != jy);
5750
5751   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5752   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5753   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5754   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5755   int leave_side_horiz = move_dir_horiz;
5756   int leave_side_vert  = move_dir_vert;
5757   int enter_side = enter_side_horiz | enter_side_vert;
5758   int leave_side = leave_side_horiz | leave_side_vert;
5759
5760   if (player->buried)           // do not reanimate dead player
5761     return;
5762
5763   if (!player_relocated)        // no need to relocate the player
5764     return;
5765
5766   if (IS_PLAYER(jx, jy))        // player already placed at new position
5767   {
5768     RemoveField(jx, jy);        // temporarily remove newly placed player
5769     DrawLevelField(jx, jy);
5770   }
5771
5772   if (player->present)
5773   {
5774     while (player->MovPos)
5775     {
5776       ScrollPlayer(player, SCROLL_GO_ON);
5777       ScrollScreen(NULL, SCROLL_GO_ON);
5778
5779       AdvanceFrameAndPlayerCounters(player->index_nr);
5780
5781       DrawPlayer(player);
5782
5783       BackToFront_WithFrameDelay(wait_delay_value);
5784     }
5785
5786     DrawPlayer(player);         // needed here only to cleanup last field
5787     DrawLevelField(player->jx, player->jy);     // remove player graphic
5788
5789     player->is_moving = FALSE;
5790   }
5791
5792   if (IS_CUSTOM_ELEMENT(old_element))
5793     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5794                                CE_LEFT_BY_PLAYER,
5795                                player->index_bit, leave_side);
5796
5797   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5798                                       CE_PLAYER_LEAVES_X,
5799                                       player->index_bit, leave_side);
5800
5801   Tile[jx][jy] = el_player;
5802   InitPlayerField(jx, jy, el_player, TRUE);
5803
5804   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5805      possible that the relocation target field did not contain a player element,
5806      but a walkable element, to which the new player was relocated -- in this
5807      case, restore that (already initialized!) element on the player field */
5808   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5809   {
5810     Tile[jx][jy] = element;     // restore previously existing element
5811   }
5812
5813   // only visually relocate centered player
5814   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy,
5815                      FALSE, level.instant_relocation);
5816
5817   TestIfPlayerTouchesBadThing(jx, jy);
5818   TestIfPlayerTouchesCustomElement(jx, jy);
5819
5820   if (IS_CUSTOM_ELEMENT(element))
5821     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5822                                player->index_bit, enter_side);
5823
5824   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5825                                       player->index_bit, enter_side);
5826
5827   if (player->is_switching)
5828   {
5829     /* ensure that relocation while still switching an element does not cause
5830        a new element to be treated as also switched directly after relocation
5831        (this is important for teleporter switches that teleport the player to
5832        a place where another teleporter switch is in the same direction, which
5833        would then incorrectly be treated as immediately switched before the
5834        direction key that caused the switch was released) */
5835
5836     player->switch_x += jx - old_jx;
5837     player->switch_y += jy - old_jy;
5838   }
5839 }
5840
5841 static void Explode(int ex, int ey, int phase, int mode)
5842 {
5843   int x, y;
5844   int last_phase;
5845   int border_element;
5846
5847   if (game.explosions_delayed)
5848   {
5849     ExplodeField[ex][ey] = mode;
5850     return;
5851   }
5852
5853   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5854   {
5855     int center_element = Tile[ex][ey];
5856     int ce_value = CustomValue[ex][ey];
5857     int ce_score = element_info[center_element].collect_score;
5858     int artwork_element, explosion_element;     // set these values later
5859
5860     // remove things displayed in background while burning dynamite
5861     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5862       Back[ex][ey] = 0;
5863
5864     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5865     {
5866       // put moving element to center field (and let it explode there)
5867       center_element = MovingOrBlocked2Element(ex, ey);
5868       RemoveMovingField(ex, ey);
5869       Tile[ex][ey] = center_element;
5870     }
5871
5872     // now "center_element" is finally determined -- set related values now
5873     artwork_element = center_element;           // for custom player artwork
5874     explosion_element = center_element;         // for custom player artwork
5875
5876     if (IS_PLAYER(ex, ey))
5877     {
5878       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5879
5880       artwork_element = stored_player[player_nr].artwork_element;
5881
5882       if (level.use_explosion_element[player_nr])
5883       {
5884         explosion_element = level.explosion_element[player_nr];
5885         artwork_element = explosion_element;
5886       }
5887     }
5888
5889     if (mode == EX_TYPE_NORMAL ||
5890         mode == EX_TYPE_CENTER ||
5891         mode == EX_TYPE_CROSS)
5892       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5893
5894     last_phase = element_info[explosion_element].explosion_delay + 1;
5895
5896     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5897     {
5898       int xx = x - ex + 1;
5899       int yy = y - ey + 1;
5900       int element;
5901
5902       if (!IN_LEV_FIELD(x, y) ||
5903           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5904           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5905         continue;
5906
5907       element = Tile[x][y];
5908
5909       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5910       {
5911         element = MovingOrBlocked2Element(x, y);
5912
5913         if (!IS_EXPLOSION_PROOF(element))
5914           RemoveMovingField(x, y);
5915       }
5916
5917       // indestructible elements can only explode in center (but not flames)
5918       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5919                                            mode == EX_TYPE_BORDER)) ||
5920           element == EL_FLAMES)
5921         continue;
5922
5923       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5924          behaviour, for example when touching a yamyam that explodes to rocks
5925          with active deadly shield, a rock is created under the player !!! */
5926       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5927 #if 0
5928       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5929           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5930            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5931 #else
5932       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5933 #endif
5934       {
5935         if (IS_ACTIVE_BOMB(element))
5936         {
5937           // re-activate things under the bomb like gate or penguin
5938           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5939           Back[x][y] = 0;
5940         }
5941
5942         continue;
5943       }
5944
5945       // save walkable background elements while explosion on same tile
5946       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5947           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5948         Back[x][y] = element;
5949
5950       // ignite explodable elements reached by other explosion
5951       if (element == EL_EXPLOSION)
5952         element = Store2[x][y];
5953
5954       if (AmoebaNr[x][y] &&
5955           (element == EL_AMOEBA_FULL ||
5956            element == EL_BD_AMOEBA ||
5957            element == EL_AMOEBA_GROWING))
5958       {
5959         AmoebaCnt[AmoebaNr[x][y]]--;
5960         AmoebaCnt2[AmoebaNr[x][y]]--;
5961       }
5962
5963       RemoveField(x, y);
5964
5965       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5966       {
5967         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5968
5969         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5970
5971         if (PLAYERINFO(ex, ey)->use_murphy)
5972           Store[x][y] = EL_EMPTY;
5973       }
5974
5975       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5976       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5977       else if (IS_PLAYER_ELEMENT(center_element))
5978         Store[x][y] = EL_EMPTY;
5979       else if (center_element == EL_YAMYAM)
5980         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5981       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5982         Store[x][y] = element_info[center_element].content.e[xx][yy];
5983 #if 1
5984       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5985       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5986       // otherwise) -- FIX THIS !!!
5987       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5988         Store[x][y] = element_info[element].content.e[1][1];
5989 #else
5990       else if (!CAN_EXPLODE(element))
5991         Store[x][y] = element_info[element].content.e[1][1];
5992 #endif
5993       else
5994         Store[x][y] = EL_EMPTY;
5995
5996       if (IS_CUSTOM_ELEMENT(center_element))
5997         Store[x][y] = (Store[x][y] == EL_CURRENT_CE_VALUE ? ce_value :
5998                        Store[x][y] == EL_CURRENT_CE_SCORE ? ce_score :
5999                        Store[x][y] >= EL_PREV_CE_8 &&
6000                        Store[x][y] <= EL_NEXT_CE_8 ?
6001                        RESOLVED_REFERENCE_ELEMENT(center_element, Store[x][y]) :
6002                        Store[x][y]);
6003
6004       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6005           center_element == EL_AMOEBA_TO_DIAMOND)
6006         Store2[x][y] = element;
6007
6008       Tile[x][y] = EL_EXPLOSION;
6009       GfxElement[x][y] = artwork_element;
6010
6011       ExplodePhase[x][y] = 1;
6012       ExplodeDelay[x][y] = last_phase;
6013
6014       Stop[x][y] = TRUE;
6015     }
6016
6017     if (center_element == EL_YAMYAM)
6018       game.yamyam_content_nr =
6019         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6020
6021     return;
6022   }
6023
6024   if (Stop[ex][ey])
6025     return;
6026
6027   x = ex;
6028   y = ey;
6029
6030   if (phase == 1)
6031     GfxFrame[x][y] = 0;         // restart explosion animation
6032
6033   last_phase = ExplodeDelay[x][y];
6034
6035   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6036
6037   // this can happen if the player leaves an explosion just in time
6038   if (GfxElement[x][y] == EL_UNDEFINED)
6039     GfxElement[x][y] = EL_EMPTY;
6040
6041   border_element = Store2[x][y];
6042   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6043     border_element = StorePlayer[x][y];
6044
6045   if (phase == element_info[border_element].ignition_delay ||
6046       phase == last_phase)
6047   {
6048     boolean border_explosion = FALSE;
6049
6050     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6051         !PLAYER_EXPLOSION_PROTECTED(x, y))
6052     {
6053       KillPlayerUnlessExplosionProtected(x, y);
6054       border_explosion = TRUE;
6055     }
6056     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6057     {
6058       Tile[x][y] = Store2[x][y];
6059       Store2[x][y] = 0;
6060       Bang(x, y);
6061       border_explosion = TRUE;
6062     }
6063     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6064     {
6065       AmoebaToDiamond(x, y);
6066       Store2[x][y] = 0;
6067       border_explosion = TRUE;
6068     }
6069
6070     // if an element just explodes due to another explosion (chain-reaction),
6071     // do not immediately end the new explosion when it was the last frame of
6072     // the explosion (as it would be done in the following "if"-statement!)
6073     if (border_explosion && phase == last_phase)
6074       return;
6075   }
6076
6077   // this can happen if the player was just killed by an explosion
6078   if (GfxElement[x][y] == EL_UNDEFINED)
6079     GfxElement[x][y] = EL_EMPTY;
6080
6081   if (phase == last_phase)
6082   {
6083     int element;
6084
6085     element = Tile[x][y] = Store[x][y];
6086     Store[x][y] = Store2[x][y] = 0;
6087     GfxElement[x][y] = EL_UNDEFINED;
6088
6089     // player can escape from explosions and might therefore be still alive
6090     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6091         element <= EL_PLAYER_IS_EXPLODING_4)
6092     {
6093       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6094       int explosion_element = EL_PLAYER_1 + player_nr;
6095       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6096       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6097
6098       if (level.use_explosion_element[player_nr])
6099         explosion_element = level.explosion_element[player_nr];
6100
6101       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6102                     element_info[explosion_element].content.e[xx][yy]);
6103     }
6104
6105     // restore probably existing indestructible background element
6106     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6107       element = Tile[x][y] = Back[x][y];
6108     Back[x][y] = 0;
6109
6110     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6111     GfxDir[x][y] = MV_NONE;
6112     ChangeDelay[x][y] = 0;
6113     ChangePage[x][y] = -1;
6114
6115     CustomValue[x][y] = 0;
6116
6117     InitField_WithBug2(x, y, FALSE);
6118
6119     TEST_DrawLevelField(x, y);
6120
6121     TestIfElementTouchesCustomElement(x, y);
6122
6123     if (GFX_CRUMBLED(element))
6124       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6125
6126     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6127       StorePlayer[x][y] = 0;
6128
6129     if (IS_PLAYER_ELEMENT(element))
6130       RelocatePlayer(x, y, element);
6131   }
6132   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6133   {
6134     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6135     int frame = getGraphicAnimationFrameXY(graphic, x, y);
6136
6137     if (phase == 1)
6138       TEST_DrawLevelFieldCrumbled(x, y);
6139
6140     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6141     {
6142       DrawLevelElement(x, y, Back[x][y]);
6143       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6144     }
6145     else if (IS_WALKABLE_UNDER(Back[x][y]))
6146     {
6147       DrawLevelGraphic(x, y, graphic, frame);
6148       DrawLevelElementThruMask(x, y, Back[x][y]);
6149     }
6150     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6151       DrawLevelGraphic(x, y, graphic, frame);
6152   }
6153 }
6154
6155 static void DynaExplode(int ex, int ey)
6156 {
6157   int i, j;
6158   int dynabomb_element = Tile[ex][ey];
6159   int dynabomb_size = 1;
6160   boolean dynabomb_xl = FALSE;
6161   struct PlayerInfo *player;
6162   struct XY *xy = xy_topdown;
6163
6164   if (IS_ACTIVE_BOMB(dynabomb_element))
6165   {
6166     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6167     dynabomb_size = player->dynabomb_size;
6168     dynabomb_xl = player->dynabomb_xl;
6169     player->dynabombs_left++;
6170   }
6171
6172   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6173
6174   for (i = 0; i < NUM_DIRECTIONS; i++)
6175   {
6176     for (j = 1; j <= dynabomb_size; j++)
6177     {
6178       int x = ex + j * xy[i].x;
6179       int y = ey + j * xy[i].y;
6180       int element;
6181
6182       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6183         break;
6184
6185       element = Tile[x][y];
6186
6187       // do not restart explosions of fields with active bombs
6188       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6189         continue;
6190
6191       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6192
6193       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6194           !IS_DIGGABLE(element) && !dynabomb_xl)
6195         break;
6196     }
6197   }
6198 }
6199
6200 void Bang(int x, int y)
6201 {
6202   int element = MovingOrBlocked2Element(x, y);
6203   int explosion_type = EX_TYPE_NORMAL;
6204
6205   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6206   {
6207     struct PlayerInfo *player = PLAYERINFO(x, y);
6208
6209     element = Tile[x][y] = player->initial_element;
6210
6211     if (level.use_explosion_element[player->index_nr])
6212     {
6213       int explosion_element = level.explosion_element[player->index_nr];
6214
6215       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6216         explosion_type = EX_TYPE_CROSS;
6217       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6218         explosion_type = EX_TYPE_CENTER;
6219     }
6220   }
6221
6222   switch (element)
6223   {
6224     case EL_BUG:
6225     case EL_SPACESHIP:
6226     case EL_BD_BUTTERFLY:
6227     case EL_BD_FIREFLY:
6228     case EL_YAMYAM:
6229     case EL_DARK_YAMYAM:
6230     case EL_ROBOT:
6231     case EL_PACMAN:
6232     case EL_MOLE:
6233       RaiseScoreElement(element);
6234       break;
6235
6236     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6237     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6238     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6239     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6240     case EL_DYNABOMB_INCREASE_NUMBER:
6241     case EL_DYNABOMB_INCREASE_SIZE:
6242     case EL_DYNABOMB_INCREASE_POWER:
6243       explosion_type = EX_TYPE_DYNA;
6244       break;
6245
6246     case EL_DC_LANDMINE:
6247       explosion_type = EX_TYPE_CENTER;
6248       break;
6249
6250     case EL_PENGUIN:
6251     case EL_LAMP:
6252     case EL_LAMP_ACTIVE:
6253     case EL_AMOEBA_TO_DIAMOND:
6254       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6255         explosion_type = EX_TYPE_CENTER;
6256       break;
6257
6258     default:
6259       if (element_info[element].explosion_type == EXPLODES_CROSS)
6260         explosion_type = EX_TYPE_CROSS;
6261       else if (element_info[element].explosion_type == EXPLODES_1X1)
6262         explosion_type = EX_TYPE_CENTER;
6263       break;
6264   }
6265
6266   if (explosion_type == EX_TYPE_DYNA)
6267     DynaExplode(x, y);
6268   else
6269     Explode(x, y, EX_PHASE_START, explosion_type);
6270
6271   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6272 }
6273
6274 static void SplashAcid(int x, int y)
6275 {
6276   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6277       (!IN_LEV_FIELD(x - 1, y - 2) ||
6278        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6279     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6280
6281   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6282       (!IN_LEV_FIELD(x + 1, y - 2) ||
6283        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6284     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6285
6286   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6287 }
6288
6289 static void InitBeltMovement(void)
6290 {
6291   static int belt_base_element[4] =
6292   {
6293     EL_CONVEYOR_BELT_1_LEFT,
6294     EL_CONVEYOR_BELT_2_LEFT,
6295     EL_CONVEYOR_BELT_3_LEFT,
6296     EL_CONVEYOR_BELT_4_LEFT
6297   };
6298   static int belt_base_active_element[4] =
6299   {
6300     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6301     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6302     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6303     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6304   };
6305
6306   int x, y, i, j;
6307
6308   // set frame order for belt animation graphic according to belt direction
6309   for (i = 0; i < NUM_BELTS; i++)
6310   {
6311     int belt_nr = i;
6312
6313     for (j = 0; j < NUM_BELT_PARTS; j++)
6314     {
6315       int element = belt_base_active_element[belt_nr] + j;
6316       int graphic_1 = el2img(element);
6317       int graphic_2 = el2panelimg(element);
6318
6319       if (game.belt_dir[i] == MV_LEFT)
6320       {
6321         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6322         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6323       }
6324       else
6325       {
6326         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6327         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6328       }
6329     }
6330   }
6331
6332   SCAN_PLAYFIELD(x, y)
6333   {
6334     int element = Tile[x][y];
6335
6336     for (i = 0; i < NUM_BELTS; i++)
6337     {
6338       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6339       {
6340         int e_belt_nr = getBeltNrFromBeltElement(element);
6341         int belt_nr = i;
6342
6343         if (e_belt_nr == belt_nr)
6344         {
6345           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6346
6347           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6348         }
6349       }
6350     }
6351   }
6352 }
6353
6354 static void ToggleBeltSwitch(int x, int y)
6355 {
6356   static int belt_base_element[4] =
6357   {
6358     EL_CONVEYOR_BELT_1_LEFT,
6359     EL_CONVEYOR_BELT_2_LEFT,
6360     EL_CONVEYOR_BELT_3_LEFT,
6361     EL_CONVEYOR_BELT_4_LEFT
6362   };
6363   static int belt_base_active_element[4] =
6364   {
6365     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6366     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6367     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6368     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6369   };
6370   static int belt_base_switch_element[4] =
6371   {
6372     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6373     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6374     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6375     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6376   };
6377   static int belt_move_dir[4] =
6378   {
6379     MV_LEFT,
6380     MV_NONE,
6381     MV_RIGHT,
6382     MV_NONE,
6383   };
6384
6385   int element = Tile[x][y];
6386   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6387   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6388   int belt_dir = belt_move_dir[belt_dir_nr];
6389   int xx, yy, i;
6390
6391   if (!IS_BELT_SWITCH(element))
6392     return;
6393
6394   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6395   game.belt_dir[belt_nr] = belt_dir;
6396
6397   if (belt_dir_nr == 3)
6398     belt_dir_nr = 1;
6399
6400   // set frame order for belt animation graphic according to belt direction
6401   for (i = 0; i < NUM_BELT_PARTS; i++)
6402   {
6403     int element = belt_base_active_element[belt_nr] + i;
6404     int graphic_1 = el2img(element);
6405     int graphic_2 = el2panelimg(element);
6406
6407     if (belt_dir == MV_LEFT)
6408     {
6409       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6410       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6411     }
6412     else
6413     {
6414       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6415       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6416     }
6417   }
6418
6419   SCAN_PLAYFIELD(xx, yy)
6420   {
6421     int element = Tile[xx][yy];
6422
6423     if (IS_BELT_SWITCH(element))
6424     {
6425       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6426
6427       if (e_belt_nr == belt_nr)
6428       {
6429         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6430         TEST_DrawLevelField(xx, yy);
6431       }
6432     }
6433     else if (IS_BELT(element) && belt_dir != MV_NONE)
6434     {
6435       int e_belt_nr = getBeltNrFromBeltElement(element);
6436
6437       if (e_belt_nr == belt_nr)
6438       {
6439         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6440
6441         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6442         TEST_DrawLevelField(xx, yy);
6443       }
6444     }
6445     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6446     {
6447       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6448
6449       if (e_belt_nr == belt_nr)
6450       {
6451         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6452
6453         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6454         TEST_DrawLevelField(xx, yy);
6455       }
6456     }
6457   }
6458 }
6459
6460 static void ToggleSwitchgateSwitch(void)
6461 {
6462   int xx, yy;
6463
6464   game.switchgate_pos = !game.switchgate_pos;
6465
6466   SCAN_PLAYFIELD(xx, yy)
6467   {
6468     int element = Tile[xx][yy];
6469
6470     if (element == EL_SWITCHGATE_SWITCH_UP)
6471     {
6472       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6473       TEST_DrawLevelField(xx, yy);
6474     }
6475     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6476     {
6477       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6478       TEST_DrawLevelField(xx, yy);
6479     }
6480     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6481     {
6482       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6483       TEST_DrawLevelField(xx, yy);
6484     }
6485     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6486     {
6487       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6488       TEST_DrawLevelField(xx, yy);
6489     }
6490     else if (element == EL_SWITCHGATE_OPEN ||
6491              element == EL_SWITCHGATE_OPENING)
6492     {
6493       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6494
6495       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6496     }
6497     else if (element == EL_SWITCHGATE_CLOSED ||
6498              element == EL_SWITCHGATE_CLOSING)
6499     {
6500       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6501
6502       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6503     }
6504   }
6505 }
6506
6507 static int getInvisibleActiveFromInvisibleElement(int element)
6508 {
6509   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6510           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6511           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6512           element);
6513 }
6514
6515 static int getInvisibleFromInvisibleActiveElement(int element)
6516 {
6517   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6518           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6519           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6520           element);
6521 }
6522
6523 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6524 {
6525   int x, y;
6526
6527   SCAN_PLAYFIELD(x, y)
6528   {
6529     int element = Tile[x][y];
6530
6531     if (element == EL_LIGHT_SWITCH &&
6532         game.light_time_left > 0)
6533     {
6534       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6535       TEST_DrawLevelField(x, y);
6536     }
6537     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6538              game.light_time_left == 0)
6539     {
6540       Tile[x][y] = EL_LIGHT_SWITCH;
6541       TEST_DrawLevelField(x, y);
6542     }
6543     else if (element == EL_EMC_DRIPPER &&
6544              game.light_time_left > 0)
6545     {
6546       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6547       TEST_DrawLevelField(x, y);
6548     }
6549     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6550              game.light_time_left == 0)
6551     {
6552       Tile[x][y] = EL_EMC_DRIPPER;
6553       TEST_DrawLevelField(x, y);
6554     }
6555     else if (element == EL_INVISIBLE_STEELWALL ||
6556              element == EL_INVISIBLE_WALL ||
6557              element == EL_INVISIBLE_SAND)
6558     {
6559       if (game.light_time_left > 0)
6560         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6561
6562       TEST_DrawLevelField(x, y);
6563
6564       // uncrumble neighbour fields, if needed
6565       if (element == EL_INVISIBLE_SAND)
6566         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6567     }
6568     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6569              element == EL_INVISIBLE_WALL_ACTIVE ||
6570              element == EL_INVISIBLE_SAND_ACTIVE)
6571     {
6572       if (game.light_time_left == 0)
6573         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6574
6575       TEST_DrawLevelField(x, y);
6576
6577       // re-crumble neighbour fields, if needed
6578       if (element == EL_INVISIBLE_SAND)
6579         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6580     }
6581   }
6582 }
6583
6584 static void RedrawAllInvisibleElementsForLenses(void)
6585 {
6586   int x, y;
6587
6588   SCAN_PLAYFIELD(x, y)
6589   {
6590     int element = Tile[x][y];
6591
6592     if (element == EL_EMC_DRIPPER &&
6593         game.lenses_time_left > 0)
6594     {
6595       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6596       TEST_DrawLevelField(x, y);
6597     }
6598     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6599              game.lenses_time_left == 0)
6600     {
6601       Tile[x][y] = EL_EMC_DRIPPER;
6602       TEST_DrawLevelField(x, y);
6603     }
6604     else if (element == EL_INVISIBLE_STEELWALL ||
6605              element == EL_INVISIBLE_WALL ||
6606              element == EL_INVISIBLE_SAND)
6607     {
6608       if (game.lenses_time_left > 0)
6609         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6610
6611       TEST_DrawLevelField(x, y);
6612
6613       // uncrumble neighbour fields, if needed
6614       if (element == EL_INVISIBLE_SAND)
6615         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6616     }
6617     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6618              element == EL_INVISIBLE_WALL_ACTIVE ||
6619              element == EL_INVISIBLE_SAND_ACTIVE)
6620     {
6621       if (game.lenses_time_left == 0)
6622         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6623
6624       TEST_DrawLevelField(x, y);
6625
6626       // re-crumble neighbour fields, if needed
6627       if (element == EL_INVISIBLE_SAND)
6628         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6629     }
6630   }
6631 }
6632
6633 static void RedrawAllInvisibleElementsForMagnifier(void)
6634 {
6635   int x, y;
6636
6637   SCAN_PLAYFIELD(x, y)
6638   {
6639     int element = Tile[x][y];
6640
6641     if (element == EL_EMC_FAKE_GRASS &&
6642         game.magnify_time_left > 0)
6643     {
6644       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6645       TEST_DrawLevelField(x, y);
6646     }
6647     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6648              game.magnify_time_left == 0)
6649     {
6650       Tile[x][y] = EL_EMC_FAKE_GRASS;
6651       TEST_DrawLevelField(x, y);
6652     }
6653     else if (IS_GATE_GRAY(element) &&
6654              game.magnify_time_left > 0)
6655     {
6656       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6657                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6658                     IS_EM_GATE_GRAY(element) ?
6659                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6660                     IS_EMC_GATE_GRAY(element) ?
6661                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6662                     IS_DC_GATE_GRAY(element) ?
6663                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6664                     element);
6665       TEST_DrawLevelField(x, y);
6666     }
6667     else if (IS_GATE_GRAY_ACTIVE(element) &&
6668              game.magnify_time_left == 0)
6669     {
6670       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6671                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6672                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6673                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6674                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6675                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6676                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6677                     EL_DC_GATE_WHITE_GRAY :
6678                     element);
6679       TEST_DrawLevelField(x, y);
6680     }
6681   }
6682 }
6683
6684 static void ToggleLightSwitch(int x, int y)
6685 {
6686   int element = Tile[x][y];
6687
6688   game.light_time_left =
6689     (element == EL_LIGHT_SWITCH ?
6690      level.time_light * FRAMES_PER_SECOND : 0);
6691
6692   RedrawAllLightSwitchesAndInvisibleElements();
6693 }
6694
6695 static void ActivateTimegateSwitch(int x, int y)
6696 {
6697   int xx, yy;
6698
6699   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6700
6701   SCAN_PLAYFIELD(xx, yy)
6702   {
6703     int element = Tile[xx][yy];
6704
6705     if (element == EL_TIMEGATE_CLOSED ||
6706         element == EL_TIMEGATE_CLOSING)
6707     {
6708       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6709       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6710     }
6711
6712     /*
6713     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6714     {
6715       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6716       TEST_DrawLevelField(xx, yy);
6717     }
6718     */
6719
6720   }
6721
6722   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6723                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6724 }
6725
6726 static void Impact(int x, int y)
6727 {
6728   boolean last_line = (y == lev_fieldy - 1);
6729   boolean object_hit = FALSE;
6730   boolean impact = (last_line || object_hit);
6731   int element = Tile[x][y];
6732   int smashed = EL_STEELWALL;
6733
6734   if (!last_line)       // check if element below was hit
6735   {
6736     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6737       return;
6738
6739     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6740                                          MovDir[x][y + 1] != MV_DOWN ||
6741                                          MovPos[x][y + 1] <= TILEY / 2));
6742
6743     // do not smash moving elements that left the smashed field in time
6744     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6745         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6746       object_hit = FALSE;
6747
6748 #if USE_QUICKSAND_IMPACT_BUGFIX
6749     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6750     {
6751       RemoveMovingField(x, y + 1);
6752       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6753       Tile[x][y + 2] = EL_ROCK;
6754       TEST_DrawLevelField(x, y + 2);
6755
6756       object_hit = TRUE;
6757     }
6758
6759     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6760     {
6761       RemoveMovingField(x, y + 1);
6762       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6763       Tile[x][y + 2] = EL_ROCK;
6764       TEST_DrawLevelField(x, y + 2);
6765
6766       object_hit = TRUE;
6767     }
6768 #endif
6769
6770     if (object_hit)
6771       smashed = MovingOrBlocked2Element(x, y + 1);
6772
6773     impact = (last_line || object_hit);
6774   }
6775
6776   if (!last_line && smashed == EL_ACID) // element falls into acid
6777   {
6778     SplashAcid(x, y + 1);
6779     return;
6780   }
6781
6782   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6783   // only reset graphic animation if graphic really changes after impact
6784   if (impact &&
6785       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6786   {
6787     ResetGfxAnimation(x, y);
6788     TEST_DrawLevelField(x, y);
6789   }
6790
6791   if (impact && CAN_EXPLODE_IMPACT(element))
6792   {
6793     Bang(x, y);
6794     return;
6795   }
6796   else if (impact && element == EL_PEARL &&
6797            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6798   {
6799     ResetGfxAnimation(x, y);
6800
6801     Tile[x][y] = EL_PEARL_BREAKING;
6802     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6803     return;
6804   }
6805   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6806   {
6807     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6808
6809     return;
6810   }
6811
6812   if (impact && element == EL_AMOEBA_DROP)
6813   {
6814     if (object_hit && IS_PLAYER(x, y + 1))
6815       KillPlayerUnlessEnemyProtected(x, y + 1);
6816     else if (object_hit && smashed == EL_PENGUIN)
6817       Bang(x, y + 1);
6818     else
6819     {
6820       Tile[x][y] = EL_AMOEBA_GROWING;
6821       Store[x][y] = EL_AMOEBA_WET;
6822
6823       ResetRandomAnimationValue(x, y);
6824     }
6825     return;
6826   }
6827
6828   if (object_hit)               // check which object was hit
6829   {
6830     if ((CAN_PASS_MAGIC_WALL(element) && 
6831          (smashed == EL_MAGIC_WALL ||
6832           smashed == EL_BD_MAGIC_WALL)) ||
6833         (CAN_PASS_DC_MAGIC_WALL(element) &&
6834          smashed == EL_DC_MAGIC_WALL))
6835     {
6836       int xx, yy;
6837       int activated_magic_wall =
6838         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6839          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6840          EL_DC_MAGIC_WALL_ACTIVE);
6841
6842       // activate magic wall / mill
6843       SCAN_PLAYFIELD(xx, yy)
6844       {
6845         if (Tile[xx][yy] == smashed)
6846           Tile[xx][yy] = activated_magic_wall;
6847       }
6848
6849       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6850       game.magic_wall_active = TRUE;
6851
6852       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6853                             SND_MAGIC_WALL_ACTIVATING :
6854                             smashed == EL_BD_MAGIC_WALL ?
6855                             SND_BD_MAGIC_WALL_ACTIVATING :
6856                             SND_DC_MAGIC_WALL_ACTIVATING));
6857     }
6858
6859     if (IS_PLAYER(x, y + 1))
6860     {
6861       if (CAN_SMASH_PLAYER(element))
6862       {
6863         KillPlayerUnlessEnemyProtected(x, y + 1);
6864         return;
6865       }
6866     }
6867     else if (smashed == EL_PENGUIN)
6868     {
6869       if (CAN_SMASH_PLAYER(element))
6870       {
6871         Bang(x, y + 1);
6872         return;
6873       }
6874     }
6875     else if (element == EL_BD_DIAMOND)
6876     {
6877       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6878       {
6879         Bang(x, y + 1);
6880         return;
6881       }
6882     }
6883     else if (((element == EL_SP_INFOTRON ||
6884                element == EL_SP_ZONK) &&
6885               (smashed == EL_SP_SNIKSNAK ||
6886                smashed == EL_SP_ELECTRON ||
6887                smashed == EL_SP_DISK_ORANGE)) ||
6888              (element == EL_SP_INFOTRON &&
6889               smashed == EL_SP_DISK_YELLOW))
6890     {
6891       Bang(x, y + 1);
6892       return;
6893     }
6894     else if (CAN_SMASH_EVERYTHING(element))
6895     {
6896       if (IS_CLASSIC_ENEMY(smashed) ||
6897           CAN_EXPLODE_SMASHED(smashed))
6898       {
6899         Bang(x, y + 1);
6900         return;
6901       }
6902       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6903       {
6904         if (smashed == EL_LAMP ||
6905             smashed == EL_LAMP_ACTIVE)
6906         {
6907           Bang(x, y + 1);
6908           return;
6909         }
6910         else if (smashed == EL_NUT)
6911         {
6912           Tile[x][y + 1] = EL_NUT_BREAKING;
6913           PlayLevelSound(x, y, SND_NUT_BREAKING);
6914           RaiseScoreElement(EL_NUT);
6915           return;
6916         }
6917         else if (smashed == EL_PEARL)
6918         {
6919           ResetGfxAnimation(x, y);
6920
6921           Tile[x][y + 1] = EL_PEARL_BREAKING;
6922           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6923           return;
6924         }
6925         else if (smashed == EL_DIAMOND)
6926         {
6927           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6928           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6929           return;
6930         }
6931         else if (IS_BELT_SWITCH(smashed))
6932         {
6933           ToggleBeltSwitch(x, y + 1);
6934         }
6935         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6936                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6937                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6938                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6939         {
6940           ToggleSwitchgateSwitch();
6941         }
6942         else if (smashed == EL_LIGHT_SWITCH ||
6943                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6944         {
6945           ToggleLightSwitch(x, y + 1);
6946         }
6947         else
6948         {
6949           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6950
6951           CheckElementChangeBySide(x, y + 1, smashed, element,
6952                                    CE_SWITCHED, CH_SIDE_TOP);
6953           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6954                                             CH_SIDE_TOP);
6955         }
6956       }
6957       else
6958       {
6959         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6960       }
6961     }
6962   }
6963
6964   // play sound of magic wall / mill
6965   if (!last_line &&
6966       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6967        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6968        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6969   {
6970     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6971       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6972     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6973       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6974     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6975       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6976
6977     return;
6978   }
6979
6980   // play sound of object that hits the ground
6981   if (last_line || object_hit)
6982     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6983 }
6984
6985 static void TurnRoundExt(int x, int y)
6986 {
6987   static struct
6988   {
6989     int dx, dy;
6990   } move_xy[] =
6991   {
6992     {  0,  0 },
6993     { -1,  0 },
6994     { +1,  0 },
6995     {  0,  0 },
6996     {  0, -1 },
6997     {  0,  0 }, { 0, 0 }, { 0, 0 },
6998     {  0, +1 }
6999   };
7000   static struct
7001   {
7002     int left, right, back;
7003   } turn[] =
7004   {
7005     { 0,        0,              0        },
7006     { MV_DOWN,  MV_UP,          MV_RIGHT },
7007     { MV_UP,    MV_DOWN,        MV_LEFT  },
7008     { 0,        0,              0        },
7009     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
7010     { 0,        0,              0        },
7011     { 0,        0,              0        },
7012     { 0,        0,              0        },
7013     { MV_RIGHT, MV_LEFT,        MV_UP    }
7014   };
7015
7016   int element = Tile[x][y];
7017   int move_pattern = element_info[element].move_pattern;
7018
7019   int old_move_dir = MovDir[x][y];
7020   int left_dir  = turn[old_move_dir].left;
7021   int right_dir = turn[old_move_dir].right;
7022   int back_dir  = turn[old_move_dir].back;
7023
7024   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7025   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7026   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7027   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7028
7029   int left_x  = x + left_dx,  left_y  = y + left_dy;
7030   int right_x = x + right_dx, right_y = y + right_dy;
7031   int move_x  = x + move_dx,  move_y  = y + move_dy;
7032
7033   int xx, yy;
7034
7035   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7036   {
7037     TestIfBadThingTouchesOtherBadThing(x, y);
7038
7039     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7040       MovDir[x][y] = right_dir;
7041     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7042       MovDir[x][y] = left_dir;
7043
7044     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7045       MovDelay[x][y] = 9;
7046     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
7047       MovDelay[x][y] = 1;
7048   }
7049   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7050   {
7051     TestIfBadThingTouchesOtherBadThing(x, y);
7052
7053     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7054       MovDir[x][y] = left_dir;
7055     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7056       MovDir[x][y] = right_dir;
7057
7058     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7059       MovDelay[x][y] = 9;
7060     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
7061       MovDelay[x][y] = 1;
7062   }
7063   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7064   {
7065     TestIfBadThingTouchesOtherBadThing(x, y);
7066
7067     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7068       MovDir[x][y] = left_dir;
7069     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7070       MovDir[x][y] = right_dir;
7071
7072     if (MovDir[x][y] != old_move_dir)
7073       MovDelay[x][y] = 9;
7074   }
7075   else if (element == EL_YAMYAM)
7076   {
7077     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7078     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7079
7080     if (can_turn_left && can_turn_right)
7081       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7082     else if (can_turn_left)
7083       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7084     else if (can_turn_right)
7085       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7086     else
7087       MovDir[x][y] = back_dir;
7088
7089     MovDelay[x][y] = 16 + 16 * RND(3);
7090   }
7091   else if (element == EL_DARK_YAMYAM)
7092   {
7093     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7094                                                          left_x, left_y);
7095     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7096                                                          right_x, right_y);
7097
7098     if (can_turn_left && can_turn_right)
7099       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7100     else if (can_turn_left)
7101       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7102     else if (can_turn_right)
7103       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7104     else
7105       MovDir[x][y] = back_dir;
7106
7107     MovDelay[x][y] = 16 + 16 * RND(3);
7108   }
7109   else if (element == EL_PACMAN)
7110   {
7111     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7112     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7113
7114     if (can_turn_left && can_turn_right)
7115       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7116     else if (can_turn_left)
7117       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7118     else if (can_turn_right)
7119       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7120     else
7121       MovDir[x][y] = back_dir;
7122
7123     MovDelay[x][y] = 6 + RND(40);
7124   }
7125   else if (element == EL_PIG)
7126   {
7127     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7128     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7129     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7130     boolean should_turn_left, should_turn_right, should_move_on;
7131     int rnd_value = 24;
7132     int rnd = RND(rnd_value);
7133
7134     should_turn_left = (can_turn_left &&
7135                         (!can_move_on ||
7136                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7137                                                    y + back_dy + left_dy)));
7138     should_turn_right = (can_turn_right &&
7139                          (!can_move_on ||
7140                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7141                                                     y + back_dy + right_dy)));
7142     should_move_on = (can_move_on &&
7143                       (!can_turn_left ||
7144                        !can_turn_right ||
7145                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7146                                                  y + move_dy + left_dy) ||
7147                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7148                                                  y + move_dy + right_dy)));
7149
7150     if (should_turn_left || should_turn_right || should_move_on)
7151     {
7152       if (should_turn_left && should_turn_right && should_move_on)
7153         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7154                         rnd < 2 * rnd_value / 3 ? right_dir :
7155                         old_move_dir);
7156       else if (should_turn_left && should_turn_right)
7157         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7158       else if (should_turn_left && should_move_on)
7159         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7160       else if (should_turn_right && should_move_on)
7161         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7162       else if (should_turn_left)
7163         MovDir[x][y] = left_dir;
7164       else if (should_turn_right)
7165         MovDir[x][y] = right_dir;
7166       else if (should_move_on)
7167         MovDir[x][y] = old_move_dir;
7168     }
7169     else if (can_move_on && rnd > rnd_value / 8)
7170       MovDir[x][y] = old_move_dir;
7171     else if (can_turn_left && can_turn_right)
7172       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7173     else if (can_turn_left && rnd > rnd_value / 8)
7174       MovDir[x][y] = left_dir;
7175     else if (can_turn_right && rnd > rnd_value/8)
7176       MovDir[x][y] = right_dir;
7177     else
7178       MovDir[x][y] = back_dir;
7179
7180     xx = x + move_xy[MovDir[x][y]].dx;
7181     yy = y + move_xy[MovDir[x][y]].dy;
7182
7183     if (!IN_LEV_FIELD(xx, yy) ||
7184         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7185       MovDir[x][y] = old_move_dir;
7186
7187     MovDelay[x][y] = 0;
7188   }
7189   else if (element == EL_DRAGON)
7190   {
7191     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7192     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7193     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7194     int rnd_value = 24;
7195     int rnd = RND(rnd_value);
7196
7197     if (can_move_on && rnd > rnd_value / 8)
7198       MovDir[x][y] = old_move_dir;
7199     else if (can_turn_left && can_turn_right)
7200       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7201     else if (can_turn_left && rnd > rnd_value / 8)
7202       MovDir[x][y] = left_dir;
7203     else if (can_turn_right && rnd > rnd_value / 8)
7204       MovDir[x][y] = right_dir;
7205     else
7206       MovDir[x][y] = back_dir;
7207
7208     xx = x + move_xy[MovDir[x][y]].dx;
7209     yy = y + move_xy[MovDir[x][y]].dy;
7210
7211     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7212       MovDir[x][y] = old_move_dir;
7213
7214     MovDelay[x][y] = 0;
7215   }
7216   else if (element == EL_MOLE)
7217   {
7218     boolean can_move_on =
7219       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7220                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7221                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7222     if (!can_move_on)
7223     {
7224       boolean can_turn_left =
7225         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7226                               IS_AMOEBOID(Tile[left_x][left_y])));
7227
7228       boolean can_turn_right =
7229         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7230                               IS_AMOEBOID(Tile[right_x][right_y])));
7231
7232       if (can_turn_left && can_turn_right)
7233         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7234       else if (can_turn_left)
7235         MovDir[x][y] = left_dir;
7236       else
7237         MovDir[x][y] = right_dir;
7238     }
7239
7240     if (MovDir[x][y] != old_move_dir)
7241       MovDelay[x][y] = 9;
7242   }
7243   else if (element == EL_BALLOON)
7244   {
7245     MovDir[x][y] = game.wind_direction;
7246     MovDelay[x][y] = 0;
7247   }
7248   else if (element == EL_SPRING)
7249   {
7250     if (MovDir[x][y] & MV_HORIZONTAL)
7251     {
7252       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7253           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7254       {
7255         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7256         ResetGfxAnimation(move_x, move_y);
7257         TEST_DrawLevelField(move_x, move_y);
7258
7259         MovDir[x][y] = back_dir;
7260       }
7261       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7262                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7263         MovDir[x][y] = MV_NONE;
7264     }
7265
7266     MovDelay[x][y] = 0;
7267   }
7268   else if (element == EL_ROBOT ||
7269            element == EL_SATELLITE ||
7270            element == EL_PENGUIN ||
7271            element == EL_EMC_ANDROID)
7272   {
7273     int attr_x = -1, attr_y = -1;
7274
7275     if (game.all_players_gone)
7276     {
7277       attr_x = game.exit_x;
7278       attr_y = game.exit_y;
7279     }
7280     else
7281     {
7282       int i;
7283
7284       for (i = 0; i < MAX_PLAYERS; i++)
7285       {
7286         struct PlayerInfo *player = &stored_player[i];
7287         int jx = player->jx, jy = player->jy;
7288
7289         if (!player->active)
7290           continue;
7291
7292         if (attr_x == -1 ||
7293             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7294         {
7295           attr_x = jx;
7296           attr_y = jy;
7297         }
7298       }
7299     }
7300
7301     if (element == EL_ROBOT &&
7302         game.robot_wheel_x >= 0 &&
7303         game.robot_wheel_y >= 0 &&
7304         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7305          game.engine_version < VERSION_IDENT(3,1,0,0)))
7306     {
7307       attr_x = game.robot_wheel_x;
7308       attr_y = game.robot_wheel_y;
7309     }
7310
7311     if (element == EL_PENGUIN)
7312     {
7313       int i;
7314       struct XY *xy = xy_topdown;
7315
7316       for (i = 0; i < NUM_DIRECTIONS; i++)
7317       {
7318         int ex = x + xy[i].x;
7319         int ey = y + xy[i].y;
7320
7321         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7322                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7323                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7324                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7325         {
7326           attr_x = ex;
7327           attr_y = ey;
7328           break;
7329         }
7330       }
7331     }
7332
7333     MovDir[x][y] = MV_NONE;
7334     if (attr_x < x)
7335       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7336     else if (attr_x > x)
7337       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7338     if (attr_y < y)
7339       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7340     else if (attr_y > y)
7341       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7342
7343     if (element == EL_ROBOT)
7344     {
7345       int newx, newy;
7346
7347       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7348         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7349       Moving2Blocked(x, y, &newx, &newy);
7350
7351       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7352         MovDelay[x][y] = 8 + 8 * !RND(3);
7353       else
7354         MovDelay[x][y] = 16;
7355     }
7356     else if (element == EL_PENGUIN)
7357     {
7358       int newx, newy;
7359
7360       MovDelay[x][y] = 1;
7361
7362       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7363       {
7364         boolean first_horiz = RND(2);
7365         int new_move_dir = MovDir[x][y];
7366
7367         MovDir[x][y] =
7368           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7369         Moving2Blocked(x, y, &newx, &newy);
7370
7371         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7372           return;
7373
7374         MovDir[x][y] =
7375           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7376         Moving2Blocked(x, y, &newx, &newy);
7377
7378         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7379           return;
7380
7381         MovDir[x][y] = old_move_dir;
7382         return;
7383       }
7384     }
7385     else if (element == EL_SATELLITE)
7386     {
7387       int newx, newy;
7388
7389       MovDelay[x][y] = 1;
7390
7391       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7392       {
7393         boolean first_horiz = RND(2);
7394         int new_move_dir = MovDir[x][y];
7395
7396         MovDir[x][y] =
7397           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7398         Moving2Blocked(x, y, &newx, &newy);
7399
7400         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7401           return;
7402
7403         MovDir[x][y] =
7404           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7405         Moving2Blocked(x, y, &newx, &newy);
7406
7407         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7408           return;
7409
7410         MovDir[x][y] = old_move_dir;
7411         return;
7412       }
7413     }
7414     else if (element == EL_EMC_ANDROID)
7415     {
7416       static int check_pos[16] =
7417       {
7418         -1,             //  0 => (invalid)
7419         7,              //  1 => MV_LEFT
7420         3,              //  2 => MV_RIGHT
7421         -1,             //  3 => (invalid)
7422         1,              //  4 =>            MV_UP
7423         0,              //  5 => MV_LEFT  | MV_UP
7424         2,              //  6 => MV_RIGHT | MV_UP
7425         -1,             //  7 => (invalid)
7426         5,              //  8 =>            MV_DOWN
7427         6,              //  9 => MV_LEFT  | MV_DOWN
7428         4,              // 10 => MV_RIGHT | MV_DOWN
7429         -1,             // 11 => (invalid)
7430         -1,             // 12 => (invalid)
7431         -1,             // 13 => (invalid)
7432         -1,             // 14 => (invalid)
7433         -1,             // 15 => (invalid)
7434       };
7435       static struct
7436       {
7437         int dx, dy;
7438         int dir;
7439       } check_xy[8] =
7440       {
7441         { -1, -1,       MV_LEFT  | MV_UP   },
7442         {  0, -1,                  MV_UP   },
7443         { +1, -1,       MV_RIGHT | MV_UP   },
7444         { +1,  0,       MV_RIGHT           },
7445         { +1, +1,       MV_RIGHT | MV_DOWN },
7446         {  0, +1,                  MV_DOWN },
7447         { -1, +1,       MV_LEFT  | MV_DOWN },
7448         { -1,  0,       MV_LEFT            },
7449       };
7450       int start_pos, check_order;
7451       boolean can_clone = FALSE;
7452       int i;
7453
7454       // check if there is any free field around current position
7455       for (i = 0; i < 8; i++)
7456       {
7457         int newx = x + check_xy[i].dx;
7458         int newy = y + check_xy[i].dy;
7459
7460         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7461         {
7462           can_clone = TRUE;
7463
7464           break;
7465         }
7466       }
7467
7468       if (can_clone)            // randomly find an element to clone
7469       {
7470         can_clone = FALSE;
7471
7472         start_pos = check_pos[RND(8)];
7473         check_order = (RND(2) ? -1 : +1);
7474
7475         for (i = 0; i < 8; i++)
7476         {
7477           int pos_raw = start_pos + i * check_order;
7478           int pos = (pos_raw + 8) % 8;
7479           int newx = x + check_xy[pos].dx;
7480           int newy = y + check_xy[pos].dy;
7481
7482           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7483           {
7484             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7485             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7486
7487             Store[x][y] = Tile[newx][newy];
7488
7489             can_clone = TRUE;
7490
7491             break;
7492           }
7493         }
7494       }
7495
7496       if (can_clone)            // randomly find a direction to move
7497       {
7498         can_clone = FALSE;
7499
7500         start_pos = check_pos[RND(8)];
7501         check_order = (RND(2) ? -1 : +1);
7502
7503         for (i = 0; i < 8; i++)
7504         {
7505           int pos_raw = start_pos + i * check_order;
7506           int pos = (pos_raw + 8) % 8;
7507           int newx = x + check_xy[pos].dx;
7508           int newy = y + check_xy[pos].dy;
7509           int new_move_dir = check_xy[pos].dir;
7510
7511           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7512           {
7513             MovDir[x][y] = new_move_dir;
7514             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7515
7516             can_clone = TRUE;
7517
7518             break;
7519           }
7520         }
7521       }
7522
7523       if (can_clone)            // cloning and moving successful
7524         return;
7525
7526       // cannot clone -- try to move towards player
7527
7528       start_pos = check_pos[MovDir[x][y] & 0x0f];
7529       check_order = (RND(2) ? -1 : +1);
7530
7531       for (i = 0; i < 3; i++)
7532       {
7533         // first check start_pos, then previous/next or (next/previous) pos
7534         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7535         int pos = (pos_raw + 8) % 8;
7536         int newx = x + check_xy[pos].dx;
7537         int newy = y + check_xy[pos].dy;
7538         int new_move_dir = check_xy[pos].dir;
7539
7540         if (IS_PLAYER(newx, newy))
7541           break;
7542
7543         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7544         {
7545           MovDir[x][y] = new_move_dir;
7546           MovDelay[x][y] = level.android_move_time * 8 + 1;
7547
7548           break;
7549         }
7550       }
7551     }
7552   }
7553   else if (move_pattern == MV_TURNING_LEFT ||
7554            move_pattern == MV_TURNING_RIGHT ||
7555            move_pattern == MV_TURNING_LEFT_RIGHT ||
7556            move_pattern == MV_TURNING_RIGHT_LEFT ||
7557            move_pattern == MV_TURNING_RANDOM ||
7558            move_pattern == MV_ALL_DIRECTIONS)
7559   {
7560     boolean can_turn_left =
7561       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7562     boolean can_turn_right =
7563       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y);
7564
7565     if (element_info[element].move_stepsize == 0)       // "not moving"
7566       return;
7567
7568     if (move_pattern == MV_TURNING_LEFT)
7569       MovDir[x][y] = left_dir;
7570     else if (move_pattern == MV_TURNING_RIGHT)
7571       MovDir[x][y] = right_dir;
7572     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7573       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7574     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7575       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7576     else if (move_pattern == MV_TURNING_RANDOM)
7577       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7578                       can_turn_right && !can_turn_left ? right_dir :
7579                       RND(2) ? left_dir : right_dir);
7580     else if (can_turn_left && can_turn_right)
7581       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7582     else if (can_turn_left)
7583       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7584     else if (can_turn_right)
7585       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7586     else
7587       MovDir[x][y] = back_dir;
7588
7589     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7590   }
7591   else if (move_pattern == MV_HORIZONTAL ||
7592            move_pattern == MV_VERTICAL)
7593   {
7594     if (move_pattern & old_move_dir)
7595       MovDir[x][y] = back_dir;
7596     else if (move_pattern == MV_HORIZONTAL)
7597       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7598     else if (move_pattern == MV_VERTICAL)
7599       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7600
7601     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7602   }
7603   else if (move_pattern & MV_ANY_DIRECTION)
7604   {
7605     MovDir[x][y] = move_pattern;
7606     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7607   }
7608   else if (move_pattern & MV_WIND_DIRECTION)
7609   {
7610     MovDir[x][y] = game.wind_direction;
7611     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7612   }
7613   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7614   {
7615     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7616       MovDir[x][y] = left_dir;
7617     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7618       MovDir[x][y] = right_dir;
7619
7620     if (MovDir[x][y] != old_move_dir)
7621       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7622   }
7623   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7624   {
7625     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7626       MovDir[x][y] = right_dir;
7627     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7628       MovDir[x][y] = left_dir;
7629
7630     if (MovDir[x][y] != old_move_dir)
7631       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7632   }
7633   else if (move_pattern == MV_TOWARDS_PLAYER ||
7634            move_pattern == MV_AWAY_FROM_PLAYER)
7635   {
7636     int attr_x = -1, attr_y = -1;
7637     int newx, newy;
7638     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7639
7640     if (game.all_players_gone)
7641     {
7642       attr_x = game.exit_x;
7643       attr_y = game.exit_y;
7644     }
7645     else
7646     {
7647       int i;
7648
7649       for (i = 0; i < MAX_PLAYERS; i++)
7650       {
7651         struct PlayerInfo *player = &stored_player[i];
7652         int jx = player->jx, jy = player->jy;
7653
7654         if (!player->active)
7655           continue;
7656
7657         if (attr_x == -1 ||
7658             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7659         {
7660           attr_x = jx;
7661           attr_y = jy;
7662         }
7663       }
7664     }
7665
7666     MovDir[x][y] = MV_NONE;
7667     if (attr_x < x)
7668       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7669     else if (attr_x > x)
7670       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7671     if (attr_y < y)
7672       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7673     else if (attr_y > y)
7674       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7675
7676     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7677
7678     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7679     {
7680       boolean first_horiz = RND(2);
7681       int new_move_dir = MovDir[x][y];
7682
7683       if (element_info[element].move_stepsize == 0)     // "not moving"
7684       {
7685         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7686         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7687
7688         return;
7689       }
7690
7691       MovDir[x][y] =
7692         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7693       Moving2Blocked(x, y, &newx, &newy);
7694
7695       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7696         return;
7697
7698       MovDir[x][y] =
7699         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7700       Moving2Blocked(x, y, &newx, &newy);
7701
7702       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7703         return;
7704
7705       MovDir[x][y] = old_move_dir;
7706     }
7707   }
7708   else if (move_pattern == MV_WHEN_PUSHED ||
7709            move_pattern == MV_WHEN_DROPPED)
7710   {
7711     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7712       MovDir[x][y] = MV_NONE;
7713
7714     MovDelay[x][y] = 0;
7715   }
7716   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7717   {
7718     struct XY *test_xy = xy_topdown;
7719     static int test_dir[4] =
7720     {
7721       MV_UP,
7722       MV_LEFT,
7723       MV_RIGHT,
7724       MV_DOWN
7725     };
7726     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7727     int move_preference = -1000000;     // start with very low preference
7728     int new_move_dir = MV_NONE;
7729     int start_test = RND(4);
7730     int i;
7731
7732     for (i = 0; i < NUM_DIRECTIONS; i++)
7733     {
7734       int j = (start_test + i) % 4;
7735       int move_dir = test_dir[j];
7736       int move_dir_preference;
7737
7738       xx = x + test_xy[j].x;
7739       yy = y + test_xy[j].y;
7740
7741       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7742           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7743       {
7744         new_move_dir = move_dir;
7745
7746         break;
7747       }
7748
7749       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7750         continue;
7751
7752       move_dir_preference = -1 * RunnerVisit[xx][yy];
7753       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7754         move_dir_preference = PlayerVisit[xx][yy];
7755
7756       if (move_dir_preference > move_preference)
7757       {
7758         // prefer field that has not been visited for the longest time
7759         move_preference = move_dir_preference;
7760         new_move_dir = move_dir;
7761       }
7762       else if (move_dir_preference == move_preference &&
7763                move_dir == old_move_dir)
7764       {
7765         // prefer last direction when all directions are preferred equally
7766         move_preference = move_dir_preference;
7767         new_move_dir = move_dir;
7768       }
7769     }
7770
7771     MovDir[x][y] = new_move_dir;
7772     if (old_move_dir != new_move_dir)
7773       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7774   }
7775 }
7776
7777 static void TurnRound(int x, int y)
7778 {
7779   int direction = MovDir[x][y];
7780
7781   TurnRoundExt(x, y);
7782
7783   GfxDir[x][y] = MovDir[x][y];
7784
7785   if (direction != MovDir[x][y])
7786     GfxFrame[x][y] = 0;
7787
7788   if (MovDelay[x][y])
7789     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7790
7791   ResetGfxFrame(x, y);
7792 }
7793
7794 static boolean JustBeingPushed(int x, int y)
7795 {
7796   int i;
7797
7798   for (i = 0; i < MAX_PLAYERS; i++)
7799   {
7800     struct PlayerInfo *player = &stored_player[i];
7801
7802     if (player->active && player->is_pushing && player->MovPos)
7803     {
7804       int next_jx = player->jx + (player->jx - player->last_jx);
7805       int next_jy = player->jy + (player->jy - player->last_jy);
7806
7807       if (x == next_jx && y == next_jy)
7808         return TRUE;
7809     }
7810   }
7811
7812   return FALSE;
7813 }
7814
7815 static void StartMoving(int x, int y)
7816 {
7817   boolean started_moving = FALSE;       // some elements can fall _and_ move
7818   int element = Tile[x][y];
7819
7820   if (Stop[x][y])
7821     return;
7822
7823   if (MovDelay[x][y] == 0)
7824     GfxAction[x][y] = ACTION_DEFAULT;
7825
7826   if (CAN_FALL(element) && y < lev_fieldy - 1)
7827   {
7828     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7829         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7830       if (JustBeingPushed(x, y))
7831         return;
7832
7833     if (element == EL_QUICKSAND_FULL)
7834     {
7835       if (IS_FREE(x, y + 1))
7836       {
7837         InitMovingField(x, y, MV_DOWN);
7838         started_moving = TRUE;
7839
7840         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7841 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7842         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7843           Store[x][y] = EL_ROCK;
7844 #else
7845         Store[x][y] = EL_ROCK;
7846 #endif
7847
7848         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7849       }
7850       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7851       {
7852         if (!MovDelay[x][y])
7853         {
7854           MovDelay[x][y] = TILEY + 1;
7855
7856           ResetGfxAnimation(x, y);
7857           ResetGfxAnimation(x, y + 1);
7858         }
7859
7860         if (MovDelay[x][y])
7861         {
7862           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7863           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7864
7865           MovDelay[x][y]--;
7866           if (MovDelay[x][y])
7867             return;
7868         }
7869
7870         Tile[x][y] = EL_QUICKSAND_EMPTY;
7871         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7872         Store[x][y + 1] = Store[x][y];
7873         Store[x][y] = 0;
7874
7875         PlayLevelSoundAction(x, y, ACTION_FILLING);
7876       }
7877       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7878       {
7879         if (!MovDelay[x][y])
7880         {
7881           MovDelay[x][y] = TILEY + 1;
7882
7883           ResetGfxAnimation(x, y);
7884           ResetGfxAnimation(x, y + 1);
7885         }
7886
7887         if (MovDelay[x][y])
7888         {
7889           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7890           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7891
7892           MovDelay[x][y]--;
7893           if (MovDelay[x][y])
7894             return;
7895         }
7896
7897         Tile[x][y] = EL_QUICKSAND_EMPTY;
7898         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7899         Store[x][y + 1] = Store[x][y];
7900         Store[x][y] = 0;
7901
7902         PlayLevelSoundAction(x, y, ACTION_FILLING);
7903       }
7904     }
7905     else if (element == EL_QUICKSAND_FAST_FULL)
7906     {
7907       if (IS_FREE(x, y + 1))
7908       {
7909         InitMovingField(x, y, MV_DOWN);
7910         started_moving = TRUE;
7911
7912         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7913 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7914         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7915           Store[x][y] = EL_ROCK;
7916 #else
7917         Store[x][y] = EL_ROCK;
7918 #endif
7919
7920         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7921       }
7922       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7923       {
7924         if (!MovDelay[x][y])
7925         {
7926           MovDelay[x][y] = TILEY + 1;
7927
7928           ResetGfxAnimation(x, y);
7929           ResetGfxAnimation(x, y + 1);
7930         }
7931
7932         if (MovDelay[x][y])
7933         {
7934           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7935           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7936
7937           MovDelay[x][y]--;
7938           if (MovDelay[x][y])
7939             return;
7940         }
7941
7942         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7943         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7944         Store[x][y + 1] = Store[x][y];
7945         Store[x][y] = 0;
7946
7947         PlayLevelSoundAction(x, y, ACTION_FILLING);
7948       }
7949       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7950       {
7951         if (!MovDelay[x][y])
7952         {
7953           MovDelay[x][y] = TILEY + 1;
7954
7955           ResetGfxAnimation(x, y);
7956           ResetGfxAnimation(x, y + 1);
7957         }
7958
7959         if (MovDelay[x][y])
7960         {
7961           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7962           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7963
7964           MovDelay[x][y]--;
7965           if (MovDelay[x][y])
7966             return;
7967         }
7968
7969         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7970         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7971         Store[x][y + 1] = Store[x][y];
7972         Store[x][y] = 0;
7973
7974         PlayLevelSoundAction(x, y, ACTION_FILLING);
7975       }
7976     }
7977     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7978              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7979     {
7980       InitMovingField(x, y, MV_DOWN);
7981       started_moving = TRUE;
7982
7983       Tile[x][y] = EL_QUICKSAND_FILLING;
7984       Store[x][y] = element;
7985
7986       PlayLevelSoundAction(x, y, ACTION_FILLING);
7987     }
7988     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7989              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7990     {
7991       InitMovingField(x, y, MV_DOWN);
7992       started_moving = TRUE;
7993
7994       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7995       Store[x][y] = element;
7996
7997       PlayLevelSoundAction(x, y, ACTION_FILLING);
7998     }
7999     else if (element == EL_MAGIC_WALL_FULL)
8000     {
8001       if (IS_FREE(x, y + 1))
8002       {
8003         InitMovingField(x, y, MV_DOWN);
8004         started_moving = TRUE;
8005
8006         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
8007         Store[x][y] = EL_CHANGED(Store[x][y]);
8008       }
8009       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8010       {
8011         if (!MovDelay[x][y])
8012           MovDelay[x][y] = TILEY / 4 + 1;
8013
8014         if (MovDelay[x][y])
8015         {
8016           MovDelay[x][y]--;
8017           if (MovDelay[x][y])
8018             return;
8019         }
8020
8021         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
8022         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
8023         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8024         Store[x][y] = 0;
8025       }
8026     }
8027     else if (element == EL_BD_MAGIC_WALL_FULL)
8028     {
8029       if (IS_FREE(x, y + 1))
8030       {
8031         InitMovingField(x, y, MV_DOWN);
8032         started_moving = TRUE;
8033
8034         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8035         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8036       }
8037       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8038       {
8039         if (!MovDelay[x][y])
8040           MovDelay[x][y] = TILEY / 4 + 1;
8041
8042         if (MovDelay[x][y])
8043         {
8044           MovDelay[x][y]--;
8045           if (MovDelay[x][y])
8046             return;
8047         }
8048
8049         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8050         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8051         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8052         Store[x][y] = 0;
8053       }
8054     }
8055     else if (element == EL_DC_MAGIC_WALL_FULL)
8056     {
8057       if (IS_FREE(x, y + 1))
8058       {
8059         InitMovingField(x, y, MV_DOWN);
8060         started_moving = TRUE;
8061
8062         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8063         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8064       }
8065       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8066       {
8067         if (!MovDelay[x][y])
8068           MovDelay[x][y] = TILEY / 4 + 1;
8069
8070         if (MovDelay[x][y])
8071         {
8072           MovDelay[x][y]--;
8073           if (MovDelay[x][y])
8074             return;
8075         }
8076
8077         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8078         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8079         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8080         Store[x][y] = 0;
8081       }
8082     }
8083     else if ((CAN_PASS_MAGIC_WALL(element) &&
8084               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8085                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8086              (CAN_PASS_DC_MAGIC_WALL(element) &&
8087               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8088
8089     {
8090       InitMovingField(x, y, MV_DOWN);
8091       started_moving = TRUE;
8092
8093       Tile[x][y] =
8094         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8095          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8096          EL_DC_MAGIC_WALL_FILLING);
8097       Store[x][y] = element;
8098     }
8099     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
8100     {
8101       SplashAcid(x, y + 1);
8102
8103       InitMovingField(x, y, MV_DOWN);
8104       started_moving = TRUE;
8105
8106       Store[x][y] = EL_ACID;
8107     }
8108     else if (
8109              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8110               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8111              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8112               CAN_FALL(element) && WasJustFalling[x][y] &&
8113               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8114
8115              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8116               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8117               (Tile[x][y + 1] == EL_BLOCKED)))
8118     {
8119       /* this is needed for a special case not covered by calling "Impact()"
8120          from "ContinueMoving()": if an element moves to a tile directly below
8121          another element which was just falling on that tile (which was empty
8122          in the previous frame), the falling element above would just stop
8123          instead of smashing the element below (in previous version, the above
8124          element was just checked for "moving" instead of "falling", resulting
8125          in incorrect smashes caused by horizontal movement of the above
8126          element; also, the case of the player being the element to smash was
8127          simply not covered here... :-/ ) */
8128
8129       CheckCollision[x][y] = 0;
8130       CheckImpact[x][y] = 0;
8131
8132       Impact(x, y);
8133     }
8134     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8135     {
8136       if (MovDir[x][y] == MV_NONE)
8137       {
8138         InitMovingField(x, y, MV_DOWN);
8139         started_moving = TRUE;
8140       }
8141     }
8142     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8143     {
8144       if (WasJustFalling[x][y]) // prevent animation from being restarted
8145         MovDir[x][y] = MV_DOWN;
8146
8147       InitMovingField(x, y, MV_DOWN);
8148       started_moving = TRUE;
8149     }
8150     else if (element == EL_AMOEBA_DROP)
8151     {
8152       Tile[x][y] = EL_AMOEBA_GROWING;
8153       Store[x][y] = EL_AMOEBA_WET;
8154     }
8155     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8156               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8157              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8158              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8159     {
8160       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8161                                 (IS_FREE(x - 1, y + 1) ||
8162                                  Tile[x - 1][y + 1] == EL_ACID));
8163       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8164                                 (IS_FREE(x + 1, y + 1) ||
8165                                  Tile[x + 1][y + 1] == EL_ACID));
8166       boolean can_fall_any  = (can_fall_left || can_fall_right);
8167       boolean can_fall_both = (can_fall_left && can_fall_right);
8168       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8169
8170       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8171       {
8172         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8173           can_fall_right = FALSE;
8174         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8175           can_fall_left = FALSE;
8176         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8177           can_fall_right = FALSE;
8178         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8179           can_fall_left = FALSE;
8180
8181         can_fall_any  = (can_fall_left || can_fall_right);
8182         can_fall_both = FALSE;
8183       }
8184
8185       if (can_fall_both)
8186       {
8187         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8188           can_fall_right = FALSE;       // slip down on left side
8189         else
8190           can_fall_left = !(can_fall_right = RND(2));
8191
8192         can_fall_both = FALSE;
8193       }
8194
8195       if (can_fall_any)
8196       {
8197         // if not determined otherwise, prefer left side for slipping down
8198         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8199         started_moving = TRUE;
8200       }
8201     }
8202     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8203     {
8204       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8205       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8206       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8207       int belt_dir = game.belt_dir[belt_nr];
8208
8209       if ((belt_dir == MV_LEFT  && left_is_free) ||
8210           (belt_dir == MV_RIGHT && right_is_free))
8211       {
8212         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8213
8214         InitMovingField(x, y, belt_dir);
8215         started_moving = TRUE;
8216
8217         Pushed[x][y] = TRUE;
8218         Pushed[nextx][y] = TRUE;
8219
8220         GfxAction[x][y] = ACTION_DEFAULT;
8221       }
8222       else
8223       {
8224         MovDir[x][y] = 0;       // if element was moving, stop it
8225       }
8226     }
8227   }
8228
8229   // not "else if" because of elements that can fall and move (EL_SPRING)
8230   if (CAN_MOVE(element) && !started_moving)
8231   {
8232     int move_pattern = element_info[element].move_pattern;
8233     int newx, newy;
8234
8235     Moving2Blocked(x, y, &newx, &newy);
8236
8237     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8238       return;
8239
8240     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8241         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8242     {
8243       WasJustMoving[x][y] = 0;
8244       CheckCollision[x][y] = 0;
8245
8246       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8247
8248       if (Tile[x][y] != element)        // element has changed
8249         return;
8250     }
8251
8252     if (!MovDelay[x][y])        // start new movement phase
8253     {
8254       // all objects that can change their move direction after each step
8255       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8256
8257       if (element != EL_YAMYAM &&
8258           element != EL_DARK_YAMYAM &&
8259           element != EL_PACMAN &&
8260           !(move_pattern & MV_ANY_DIRECTION) &&
8261           move_pattern != MV_TURNING_LEFT &&
8262           move_pattern != MV_TURNING_RIGHT &&
8263           move_pattern != MV_TURNING_LEFT_RIGHT &&
8264           move_pattern != MV_TURNING_RIGHT_LEFT &&
8265           move_pattern != MV_TURNING_RANDOM)
8266       {
8267         TurnRound(x, y);
8268
8269         if (MovDelay[x][y] && (element == EL_BUG ||
8270                                element == EL_SPACESHIP ||
8271                                element == EL_SP_SNIKSNAK ||
8272                                element == EL_SP_ELECTRON ||
8273                                element == EL_MOLE))
8274           TEST_DrawLevelField(x, y);
8275       }
8276     }
8277
8278     if (MovDelay[x][y])         // wait some time before next movement
8279     {
8280       MovDelay[x][y]--;
8281
8282       if (element == EL_ROBOT ||
8283           element == EL_YAMYAM ||
8284           element == EL_DARK_YAMYAM)
8285       {
8286         DrawLevelElementAnimationIfNeeded(x, y, element);
8287         PlayLevelSoundAction(x, y, ACTION_WAITING);
8288       }
8289       else if (element == EL_SP_ELECTRON)
8290         DrawLevelElementAnimationIfNeeded(x, y, element);
8291       else if (element == EL_DRAGON)
8292       {
8293         int i;
8294         int dir = MovDir[x][y];
8295         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8296         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8297         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8298                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8299                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8300                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8301         int frame = getGraphicAnimationFrameXY(graphic, x, y);
8302
8303         GfxAction[x][y] = ACTION_ATTACKING;
8304
8305         if (IS_PLAYER(x, y))
8306           DrawPlayerField(x, y);
8307         else
8308           TEST_DrawLevelField(x, y);
8309
8310         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8311
8312         for (i = 1; i <= 3; i++)
8313         {
8314           int xx = x + i * dx;
8315           int yy = y + i * dy;
8316           int sx = SCREENX(xx);
8317           int sy = SCREENY(yy);
8318           int flame_graphic = graphic + (i - 1);
8319
8320           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8321             break;
8322
8323           if (MovDelay[x][y])
8324           {
8325             int flamed = MovingOrBlocked2Element(xx, yy);
8326
8327             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8328               Bang(xx, yy);
8329             else
8330               RemoveMovingField(xx, yy);
8331
8332             ChangeDelay[xx][yy] = 0;
8333
8334             Tile[xx][yy] = EL_FLAMES;
8335
8336             if (IN_SCR_FIELD(sx, sy))
8337             {
8338               TEST_DrawLevelFieldCrumbled(xx, yy);
8339               DrawScreenGraphic(sx, sy, flame_graphic, frame);
8340             }
8341           }
8342           else
8343           {
8344             if (Tile[xx][yy] == EL_FLAMES)
8345               Tile[xx][yy] = EL_EMPTY;
8346             TEST_DrawLevelField(xx, yy);
8347           }
8348         }
8349       }
8350
8351       if (MovDelay[x][y])       // element still has to wait some time
8352       {
8353         PlayLevelSoundAction(x, y, ACTION_WAITING);
8354
8355         return;
8356       }
8357     }
8358
8359     // now make next step
8360
8361     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8362
8363     if (DONT_COLLIDE_WITH(element) &&
8364         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8365         !PLAYER_ENEMY_PROTECTED(newx, newy))
8366     {
8367       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8368
8369       return;
8370     }
8371
8372     else if (CAN_MOVE_INTO_ACID(element) &&
8373              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8374              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8375              (MovDir[x][y] == MV_DOWN ||
8376               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8377     {
8378       SplashAcid(newx, newy);
8379       Store[x][y] = EL_ACID;
8380     }
8381     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8382     {
8383       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8384           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8385           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8386           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8387       {
8388         RemoveField(x, y);
8389         TEST_DrawLevelField(x, y);
8390
8391         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8392         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8393           DrawGraphicThruMask(SCREENX(newx), SCREENY(newy), el2img(element), 0);
8394
8395         game.friends_still_needed--;
8396         if (!game.friends_still_needed &&
8397             !game.GameOver &&
8398             game.all_players_gone)
8399           LevelSolved();
8400
8401         return;
8402       }
8403       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8404       {
8405         if (DigField(local_player, x, y, newx, newy, 0, 0, DF_DIG) == MP_MOVING)
8406           TEST_DrawLevelField(newx, newy);
8407         else
8408           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8409       }
8410       else if (!IS_FREE(newx, newy))
8411       {
8412         GfxAction[x][y] = ACTION_WAITING;
8413
8414         if (IS_PLAYER(x, y))
8415           DrawPlayerField(x, y);
8416         else
8417           TEST_DrawLevelField(x, y);
8418
8419         return;
8420       }
8421     }
8422     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8423     {
8424       if (IS_FOOD_PIG(Tile[newx][newy]))
8425       {
8426         if (IS_MOVING(newx, newy))
8427           RemoveMovingField(newx, newy);
8428         else
8429         {
8430           Tile[newx][newy] = EL_EMPTY;
8431           TEST_DrawLevelField(newx, newy);
8432         }
8433
8434         PlayLevelSound(x, y, SND_PIG_DIGGING);
8435       }
8436       else if (!IS_FREE(newx, newy))
8437       {
8438         if (IS_PLAYER(x, y))
8439           DrawPlayerField(x, y);
8440         else
8441           TEST_DrawLevelField(x, y);
8442
8443         return;
8444       }
8445     }
8446     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8447     {
8448       if (Store[x][y] != EL_EMPTY)
8449       {
8450         boolean can_clone = FALSE;
8451         int xx, yy;
8452
8453         // check if element to clone is still there
8454         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8455         {
8456           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8457           {
8458             can_clone = TRUE;
8459
8460             break;
8461           }
8462         }
8463
8464         // cannot clone or target field not free anymore -- do not clone
8465         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8466           Store[x][y] = EL_EMPTY;
8467       }
8468
8469       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8470       {
8471         if (IS_MV_DIAGONAL(MovDir[x][y]))
8472         {
8473           int diagonal_move_dir = MovDir[x][y];
8474           int stored = Store[x][y];
8475           int change_delay = 8;
8476           int graphic;
8477
8478           // android is moving diagonally
8479
8480           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8481
8482           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8483           GfxElement[x][y] = EL_EMC_ANDROID;
8484           GfxAction[x][y] = ACTION_SHRINKING;
8485           GfxDir[x][y] = diagonal_move_dir;
8486           ChangeDelay[x][y] = change_delay;
8487
8488           if (Store[x][y] == EL_EMPTY)
8489             Store[x][y] = GfxElementEmpty[x][y];
8490
8491           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8492                                    GfxDir[x][y]);
8493
8494           DrawLevelGraphicAnimation(x, y, graphic);
8495           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8496
8497           if (Tile[newx][newy] == EL_ACID)
8498           {
8499             SplashAcid(newx, newy);
8500
8501             return;
8502           }
8503
8504           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8505
8506           Store[newx][newy] = EL_EMC_ANDROID;
8507           GfxElement[newx][newy] = EL_EMC_ANDROID;
8508           GfxAction[newx][newy] = ACTION_GROWING;
8509           GfxDir[newx][newy] = diagonal_move_dir;
8510           ChangeDelay[newx][newy] = change_delay;
8511
8512           graphic = el_act_dir2img(GfxElement[newx][newy],
8513                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8514
8515           DrawLevelGraphicAnimation(newx, newy, graphic);
8516           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8517
8518           return;
8519         }
8520         else
8521         {
8522           Tile[newx][newy] = EL_EMPTY;
8523           TEST_DrawLevelField(newx, newy);
8524
8525           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8526         }
8527       }
8528       else if (!IS_FREE(newx, newy))
8529       {
8530         return;
8531       }
8532     }
8533     else if (IS_CUSTOM_ELEMENT(element) &&
8534              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8535     {
8536       if (!DigFieldByCE(newx, newy, element))
8537         return;
8538
8539       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8540       {
8541         RunnerVisit[x][y] = FrameCounter;
8542         PlayerVisit[x][y] /= 8;         // expire player visit path
8543       }
8544     }
8545     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8546     {
8547       if (!IS_FREE(newx, newy))
8548       {
8549         if (IS_PLAYER(x, y))
8550           DrawPlayerField(x, y);
8551         else
8552           TEST_DrawLevelField(x, y);
8553
8554         return;
8555       }
8556       else
8557       {
8558         boolean wanna_flame = !RND(10);
8559         int dx = newx - x, dy = newy - y;
8560         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8561         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8562         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8563                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8564         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8565                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8566
8567         if ((wanna_flame ||
8568              IS_CLASSIC_ENEMY(element1) ||
8569              IS_CLASSIC_ENEMY(element2)) &&
8570             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8571             element1 != EL_FLAMES && element2 != EL_FLAMES)
8572         {
8573           ResetGfxAnimation(x, y);
8574           GfxAction[x][y] = ACTION_ATTACKING;
8575
8576           if (IS_PLAYER(x, y))
8577             DrawPlayerField(x, y);
8578           else
8579             TEST_DrawLevelField(x, y);
8580
8581           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8582
8583           MovDelay[x][y] = 50;
8584
8585           Tile[newx][newy] = EL_FLAMES;
8586           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8587             Tile[newx1][newy1] = EL_FLAMES;
8588           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8589             Tile[newx2][newy2] = EL_FLAMES;
8590
8591           return;
8592         }
8593       }
8594     }
8595     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8596              Tile[newx][newy] == EL_DIAMOND)
8597     {
8598       if (IS_MOVING(newx, newy))
8599         RemoveMovingField(newx, newy);
8600       else
8601       {
8602         Tile[newx][newy] = EL_EMPTY;
8603         TEST_DrawLevelField(newx, newy);
8604       }
8605
8606       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8607     }
8608     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8609              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8610     {
8611       if (AmoebaNr[newx][newy])
8612       {
8613         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8614         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8615             Tile[newx][newy] == EL_BD_AMOEBA)
8616           AmoebaCnt[AmoebaNr[newx][newy]]--;
8617       }
8618
8619       if (IS_MOVING(newx, newy))
8620       {
8621         RemoveMovingField(newx, newy);
8622       }
8623       else
8624       {
8625         Tile[newx][newy] = EL_EMPTY;
8626         TEST_DrawLevelField(newx, newy);
8627       }
8628
8629       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8630     }
8631     else if ((element == EL_PACMAN || element == EL_MOLE)
8632              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8633     {
8634       if (AmoebaNr[newx][newy])
8635       {
8636         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8637         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8638             Tile[newx][newy] == EL_BD_AMOEBA)
8639           AmoebaCnt[AmoebaNr[newx][newy]]--;
8640       }
8641
8642       if (element == EL_MOLE)
8643       {
8644         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8645         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8646
8647         ResetGfxAnimation(x, y);
8648         GfxAction[x][y] = ACTION_DIGGING;
8649         TEST_DrawLevelField(x, y);
8650
8651         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8652
8653         return;                         // wait for shrinking amoeba
8654       }
8655       else      // element == EL_PACMAN
8656       {
8657         Tile[newx][newy] = EL_EMPTY;
8658         TEST_DrawLevelField(newx, newy);
8659         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8660       }
8661     }
8662     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8663              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8664               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8665     {
8666       // wait for shrinking amoeba to completely disappear
8667       return;
8668     }
8669     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8670     {
8671       // object was running against a wall
8672
8673       TurnRound(x, y);
8674
8675       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8676         DrawLevelElementAnimation(x, y, element);
8677
8678       if (DONT_TOUCH(element))
8679         TestIfBadThingTouchesPlayer(x, y);
8680
8681       return;
8682     }
8683
8684     InitMovingField(x, y, MovDir[x][y]);
8685
8686     PlayLevelSoundAction(x, y, ACTION_MOVING);
8687   }
8688
8689   if (MovDir[x][y])
8690     ContinueMoving(x, y);
8691 }
8692
8693 void ContinueMoving(int x, int y)
8694 {
8695   int element = Tile[x][y];
8696   struct ElementInfo *ei = &element_info[element];
8697   int direction = MovDir[x][y];
8698   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8699   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8700   int newx = x + dx, newy = y + dy;
8701   int stored = Store[x][y];
8702   int stored_new = Store[newx][newy];
8703   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8704   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8705   boolean last_line = (newy == lev_fieldy - 1);
8706   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8707
8708   if (pushed_by_player)         // special case: moving object pushed by player
8709   {
8710     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x, y)->MovPos));
8711   }
8712   else if (use_step_delay)      // special case: moving object has step delay
8713   {
8714     if (!MovDelay[x][y])
8715       MovPos[x][y] += getElementMoveStepsize(x, y);
8716
8717     if (MovDelay[x][y])
8718       MovDelay[x][y]--;
8719     else
8720       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8721
8722     if (MovDelay[x][y])
8723     {
8724       TEST_DrawLevelField(x, y);
8725
8726       return;   // element is still waiting
8727     }
8728   }
8729   else                          // normal case: generically moving object
8730   {
8731     MovPos[x][y] += getElementMoveStepsize(x, y);
8732   }
8733
8734   if (ABS(MovPos[x][y]) < TILEX)
8735   {
8736     TEST_DrawLevelField(x, y);
8737
8738     return;     // element is still moving
8739   }
8740
8741   // element reached destination field
8742
8743   Tile[x][y] = EL_EMPTY;
8744   Tile[newx][newy] = element;
8745   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8746
8747   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8748   {
8749     element = Tile[newx][newy] = EL_ACID;
8750   }
8751   else if (element == EL_MOLE)
8752   {
8753     Tile[x][y] = EL_SAND;
8754
8755     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8756   }
8757   else if (element == EL_QUICKSAND_FILLING)
8758   {
8759     element = Tile[newx][newy] = get_next_element(element);
8760     Store[newx][newy] = Store[x][y];
8761   }
8762   else if (element == EL_QUICKSAND_EMPTYING)
8763   {
8764     Tile[x][y] = get_next_element(element);
8765     element = Tile[newx][newy] = Store[x][y];
8766   }
8767   else if (element == EL_QUICKSAND_FAST_FILLING)
8768   {
8769     element = Tile[newx][newy] = get_next_element(element);
8770     Store[newx][newy] = Store[x][y];
8771   }
8772   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8773   {
8774     Tile[x][y] = get_next_element(element);
8775     element = Tile[newx][newy] = Store[x][y];
8776   }
8777   else if (element == EL_MAGIC_WALL_FILLING)
8778   {
8779     element = Tile[newx][newy] = get_next_element(element);
8780     if (!game.magic_wall_active)
8781       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8782     Store[newx][newy] = Store[x][y];
8783   }
8784   else if (element == EL_MAGIC_WALL_EMPTYING)
8785   {
8786     Tile[x][y] = get_next_element(element);
8787     if (!game.magic_wall_active)
8788       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8789     element = Tile[newx][newy] = Store[x][y];
8790
8791     InitField(newx, newy, FALSE);
8792   }
8793   else if (element == EL_BD_MAGIC_WALL_FILLING)
8794   {
8795     element = Tile[newx][newy] = get_next_element(element);
8796     if (!game.magic_wall_active)
8797       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8798     Store[newx][newy] = Store[x][y];
8799   }
8800   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8801   {
8802     Tile[x][y] = get_next_element(element);
8803     if (!game.magic_wall_active)
8804       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8805     element = Tile[newx][newy] = Store[x][y];
8806
8807     InitField(newx, newy, FALSE);
8808   }
8809   else if (element == EL_DC_MAGIC_WALL_FILLING)
8810   {
8811     element = Tile[newx][newy] = get_next_element(element);
8812     if (!game.magic_wall_active)
8813       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8814     Store[newx][newy] = Store[x][y];
8815   }
8816   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8817   {
8818     Tile[x][y] = get_next_element(element);
8819     if (!game.magic_wall_active)
8820       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8821     element = Tile[newx][newy] = Store[x][y];
8822
8823     InitField(newx, newy, FALSE);
8824   }
8825   else if (element == EL_AMOEBA_DROPPING)
8826   {
8827     Tile[x][y] = get_next_element(element);
8828     element = Tile[newx][newy] = Store[x][y];
8829   }
8830   else if (element == EL_SOKOBAN_OBJECT)
8831   {
8832     if (Back[x][y])
8833       Tile[x][y] = Back[x][y];
8834
8835     if (Back[newx][newy])
8836       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8837
8838     Back[x][y] = Back[newx][newy] = 0;
8839   }
8840
8841   Store[x][y] = EL_EMPTY;
8842   MovPos[x][y] = 0;
8843   MovDir[x][y] = 0;
8844   MovDelay[x][y] = 0;
8845
8846   MovDelay[newx][newy] = 0;
8847
8848   if (CAN_CHANGE_OR_HAS_ACTION(element))
8849   {
8850     // copy element change control values to new field
8851     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8852     ChangePage[newx][newy]  = ChangePage[x][y];
8853     ChangeCount[newx][newy] = ChangeCount[x][y];
8854     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8855   }
8856
8857   CustomValue[newx][newy] = CustomValue[x][y];
8858
8859   ChangeDelay[x][y] = 0;
8860   ChangePage[x][y] = -1;
8861   ChangeCount[x][y] = 0;
8862   ChangeEvent[x][y] = -1;
8863
8864   CustomValue[x][y] = 0;
8865
8866   // copy animation control values to new field
8867   GfxFrame[newx][newy]  = GfxFrame[x][y];
8868   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8869   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8870   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8871
8872   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8873
8874   // some elements can leave other elements behind after moving
8875   if (ei->move_leave_element != EL_EMPTY &&
8876       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8877       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8878   {
8879     int move_leave_element = ei->move_leave_element;
8880
8881     // this makes it possible to leave the removed element again
8882     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8883       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8884
8885     Tile[x][y] = move_leave_element;
8886
8887     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8888       MovDir[x][y] = direction;
8889
8890     InitField(x, y, FALSE);
8891
8892     if (GFX_CRUMBLED(Tile[x][y]))
8893       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8894
8895     if (IS_PLAYER_ELEMENT(move_leave_element))
8896       RelocatePlayer(x, y, move_leave_element);
8897   }
8898
8899   // do this after checking for left-behind element
8900   ResetGfxAnimation(x, y);      // reset animation values for old field
8901
8902   if (!CAN_MOVE(element) ||
8903       (CAN_FALL(element) && direction == MV_DOWN &&
8904        (element == EL_SPRING ||
8905         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8906         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8907     GfxDir[x][y] = MovDir[newx][newy] = 0;
8908
8909   TEST_DrawLevelField(x, y);
8910   TEST_DrawLevelField(newx, newy);
8911
8912   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8913
8914   // prevent pushed element from moving on in pushed direction
8915   if (pushed_by_player && CAN_MOVE(element) &&
8916       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8917       !(element_info[element].move_pattern & direction))
8918     TurnRound(newx, newy);
8919
8920   // prevent elements on conveyor belt from moving on in last direction
8921   if (pushed_by_conveyor && CAN_FALL(element) &&
8922       direction & MV_HORIZONTAL)
8923     MovDir[newx][newy] = 0;
8924
8925   if (!pushed_by_player)
8926   {
8927     int nextx = newx + dx, nexty = newy + dy;
8928     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8929
8930     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8931
8932     if (CAN_FALL(element) && direction == MV_DOWN)
8933       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8934
8935     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8936       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8937
8938     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8939       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8940   }
8941
8942   if (DONT_TOUCH(element))      // object may be nasty to player or others
8943   {
8944     TestIfBadThingTouchesPlayer(newx, newy);
8945     TestIfBadThingTouchesFriend(newx, newy);
8946
8947     if (!IS_CUSTOM_ELEMENT(element))
8948       TestIfBadThingTouchesOtherBadThing(newx, newy);
8949   }
8950   else if (element == EL_PENGUIN)
8951     TestIfFriendTouchesBadThing(newx, newy);
8952
8953   if (DONT_GET_HIT_BY(element))
8954   {
8955     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8956   }
8957
8958   // give the player one last chance (one more frame) to move away
8959   if (CAN_FALL(element) && direction == MV_DOWN &&
8960       (last_line || (!IS_FREE(x, newy + 1) &&
8961                      (!IS_PLAYER(x, newy + 1) ||
8962                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8963     Impact(x, newy);
8964
8965   if (pushed_by_player && !game.use_change_when_pushing_bug)
8966   {
8967     int push_side = MV_DIR_OPPOSITE(direction);
8968     struct PlayerInfo *player = PLAYERINFO(x, y);
8969
8970     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8971                                player->index_bit, push_side);
8972     CheckTriggeredElementChangeByPlayer(newx, newy, element, CE_PLAYER_PUSHES_X,
8973                                         player->index_bit, push_side);
8974   }
8975
8976   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8977     MovDelay[newx][newy] = 1;
8978
8979   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8980
8981   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8982   TestIfElementHitsCustomElement(newx, newy, direction);
8983   TestIfPlayerTouchesCustomElement(newx, newy);
8984   TestIfElementTouchesCustomElement(newx, newy);
8985
8986   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8987       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8988     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8989                              MV_DIR_OPPOSITE(direction));
8990 }
8991
8992 int AmoebaNeighbourNr(int ax, int ay)
8993 {
8994   int i;
8995   int element = Tile[ax][ay];
8996   int group_nr = 0;
8997   struct XY *xy = xy_topdown;
8998
8999   for (i = 0; i < NUM_DIRECTIONS; i++)
9000   {
9001     int x = ax + xy[i].x;
9002     int y = ay + xy[i].y;
9003
9004     if (!IN_LEV_FIELD(x, y))
9005       continue;
9006
9007     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
9008       group_nr = AmoebaNr[x][y];
9009   }
9010
9011   return group_nr;
9012 }
9013
9014 static void AmoebaMerge(int ax, int ay)
9015 {
9016   int i, x, y, xx, yy;
9017   int new_group_nr = AmoebaNr[ax][ay];
9018   struct XY *xy = xy_topdown;
9019
9020   if (new_group_nr == 0)
9021     return;
9022
9023   for (i = 0; i < NUM_DIRECTIONS; i++)
9024   {
9025     x = ax + xy[i].x;
9026     y = ay + xy[i].y;
9027
9028     if (!IN_LEV_FIELD(x, y))
9029       continue;
9030
9031     if ((Tile[x][y] == EL_AMOEBA_FULL ||
9032          Tile[x][y] == EL_BD_AMOEBA ||
9033          Tile[x][y] == EL_AMOEBA_DEAD) &&
9034         AmoebaNr[x][y] != new_group_nr)
9035     {
9036       int old_group_nr = AmoebaNr[x][y];
9037
9038       if (old_group_nr == 0)
9039         return;
9040
9041       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9042       AmoebaCnt[old_group_nr] = 0;
9043       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9044       AmoebaCnt2[old_group_nr] = 0;
9045
9046       SCAN_PLAYFIELD(xx, yy)
9047       {
9048         if (AmoebaNr[xx][yy] == old_group_nr)
9049           AmoebaNr[xx][yy] = new_group_nr;
9050       }
9051     }
9052   }
9053 }
9054
9055 void AmoebaToDiamond(int ax, int ay)
9056 {
9057   int i, x, y;
9058
9059   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
9060   {
9061     int group_nr = AmoebaNr[ax][ay];
9062
9063 #ifdef DEBUG
9064     if (group_nr == 0)
9065     {
9066       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
9067       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
9068
9069       return;
9070     }
9071 #endif
9072
9073     SCAN_PLAYFIELD(x, y)
9074     {
9075       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9076       {
9077         AmoebaNr[x][y] = 0;
9078         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
9079       }
9080     }
9081
9082     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9083                             SND_AMOEBA_TURNING_TO_GEM :
9084                             SND_AMOEBA_TURNING_TO_ROCK));
9085     Bang(ax, ay);
9086   }
9087   else
9088   {
9089     struct XY *xy = xy_topdown;
9090
9091     for (i = 0; i < NUM_DIRECTIONS; i++)
9092     {
9093       x = ax + xy[i].x;
9094       y = ay + xy[i].y;
9095
9096       if (!IN_LEV_FIELD(x, y))
9097         continue;
9098
9099       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9100       {
9101         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9102                               SND_AMOEBA_TURNING_TO_GEM :
9103                               SND_AMOEBA_TURNING_TO_ROCK));
9104         Bang(x, y);
9105       }
9106     }
9107   }
9108 }
9109
9110 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9111 {
9112   int x, y;
9113   int group_nr = AmoebaNr[ax][ay];
9114   boolean done = FALSE;
9115
9116 #ifdef DEBUG
9117   if (group_nr == 0)
9118   {
9119     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9120     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9121
9122     return;
9123   }
9124 #endif
9125
9126   SCAN_PLAYFIELD(x, y)
9127   {
9128     if (AmoebaNr[x][y] == group_nr &&
9129         (Tile[x][y] == EL_AMOEBA_DEAD ||
9130          Tile[x][y] == EL_BD_AMOEBA ||
9131          Tile[x][y] == EL_AMOEBA_GROWING))
9132     {
9133       AmoebaNr[x][y] = 0;
9134       Tile[x][y] = new_element;
9135       InitField(x, y, FALSE);
9136       TEST_DrawLevelField(x, y);
9137       done = TRUE;
9138     }
9139   }
9140
9141   if (done)
9142     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9143                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9144                             SND_BD_AMOEBA_TURNING_TO_GEM));
9145 }
9146
9147 static void AmoebaGrowing(int x, int y)
9148 {
9149   static DelayCounter sound_delay = { 0 };
9150
9151   if (!MovDelay[x][y])          // start new growing cycle
9152   {
9153     MovDelay[x][y] = 7;
9154
9155     if (DelayReached(&sound_delay))
9156     {
9157       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9158       sound_delay.value = 30;
9159     }
9160   }
9161
9162   if (MovDelay[x][y])           // wait some time before growing bigger
9163   {
9164     MovDelay[x][y]--;
9165     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9166     {
9167       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9168                                            6 - MovDelay[x][y]);
9169
9170       DrawLevelGraphic(x, y, IMG_AMOEBA_GROWING, frame);
9171     }
9172
9173     if (!MovDelay[x][y])
9174     {
9175       Tile[x][y] = Store[x][y];
9176       Store[x][y] = 0;
9177       TEST_DrawLevelField(x, y);
9178     }
9179   }
9180 }
9181
9182 static void AmoebaShrinking(int x, int y)
9183 {
9184   static DelayCounter sound_delay = { 0 };
9185
9186   if (!MovDelay[x][y])          // start new shrinking cycle
9187   {
9188     MovDelay[x][y] = 7;
9189
9190     if (DelayReached(&sound_delay))
9191       sound_delay.value = 30;
9192   }
9193
9194   if (MovDelay[x][y])           // wait some time before shrinking
9195   {
9196     MovDelay[x][y]--;
9197     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9198     {
9199       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9200                                            6 - MovDelay[x][y]);
9201
9202       DrawLevelGraphic(x, y, IMG_AMOEBA_SHRINKING, frame);
9203     }
9204
9205     if (!MovDelay[x][y])
9206     {
9207       Tile[x][y] = EL_EMPTY;
9208       TEST_DrawLevelField(x, y);
9209
9210       // don't let mole enter this field in this cycle;
9211       // (give priority to objects falling to this field from above)
9212       Stop[x][y] = TRUE;
9213     }
9214   }
9215 }
9216
9217 static void AmoebaReproduce(int ax, int ay)
9218 {
9219   int i;
9220   int element = Tile[ax][ay];
9221   int graphic = el2img(element);
9222   int newax = ax, neway = ay;
9223   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9224   struct XY *xy = xy_topdown;
9225
9226   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9227   {
9228     Tile[ax][ay] = EL_AMOEBA_DEAD;
9229     TEST_DrawLevelField(ax, ay);
9230     return;
9231   }
9232
9233   if (IS_ANIMATED(graphic))
9234     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9235
9236   if (!MovDelay[ax][ay])        // start making new amoeba field
9237     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9238
9239   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9240   {
9241     MovDelay[ax][ay]--;
9242     if (MovDelay[ax][ay])
9243       return;
9244   }
9245
9246   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9247   {
9248     int start = RND(4);
9249     int x = ax + xy[start].x;
9250     int y = ay + xy[start].y;
9251
9252     if (!IN_LEV_FIELD(x, y))
9253       return;
9254
9255     if (IS_FREE(x, y) ||
9256         CAN_GROW_INTO(Tile[x][y]) ||
9257         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9258         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9259     {
9260       newax = x;
9261       neway = y;
9262     }
9263
9264     if (newax == ax && neway == ay)
9265       return;
9266   }
9267   else                          // normal or "filled" (BD style) amoeba
9268   {
9269     int start = RND(4);
9270     boolean waiting_for_player = FALSE;
9271
9272     for (i = 0; i < NUM_DIRECTIONS; i++)
9273     {
9274       int j = (start + i) % 4;
9275       int x = ax + xy[j].x;
9276       int y = ay + xy[j].y;
9277
9278       if (!IN_LEV_FIELD(x, y))
9279         continue;
9280
9281       if (IS_FREE(x, y) ||
9282           CAN_GROW_INTO(Tile[x][y]) ||
9283           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9284           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9285       {
9286         newax = x;
9287         neway = y;
9288         break;
9289       }
9290       else if (IS_PLAYER(x, y))
9291         waiting_for_player = TRUE;
9292     }
9293
9294     if (newax == ax && neway == ay)             // amoeba cannot grow
9295     {
9296       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9297       {
9298         Tile[ax][ay] = EL_AMOEBA_DEAD;
9299         TEST_DrawLevelField(ax, ay);
9300         AmoebaCnt[AmoebaNr[ax][ay]]--;
9301
9302         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9303         {
9304           if (element == EL_AMOEBA_FULL)
9305             AmoebaToDiamond(ax, ay);
9306           else if (element == EL_BD_AMOEBA)
9307             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9308         }
9309       }
9310       return;
9311     }
9312     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9313     {
9314       // amoeba gets larger by growing in some direction
9315
9316       int new_group_nr = AmoebaNr[ax][ay];
9317
9318 #ifdef DEBUG
9319   if (new_group_nr == 0)
9320   {
9321     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9322           newax, neway);
9323     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9324
9325     return;
9326   }
9327 #endif
9328
9329       AmoebaNr[newax][neway] = new_group_nr;
9330       AmoebaCnt[new_group_nr]++;
9331       AmoebaCnt2[new_group_nr]++;
9332
9333       // if amoeba touches other amoeba(s) after growing, unify them
9334       AmoebaMerge(newax, neway);
9335
9336       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9337       {
9338         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9339         return;
9340       }
9341     }
9342   }
9343
9344   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9345       (neway == lev_fieldy - 1 && newax != ax))
9346   {
9347     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9348     Store[newax][neway] = element;
9349   }
9350   else if (neway == ay || element == EL_EMC_DRIPPER)
9351   {
9352     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9353
9354     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9355   }
9356   else
9357   {
9358     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9359     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9360     Store[ax][ay] = EL_AMOEBA_DROP;
9361     ContinueMoving(ax, ay);
9362     return;
9363   }
9364
9365   TEST_DrawLevelField(newax, neway);
9366 }
9367
9368 static void Life(int ax, int ay)
9369 {
9370   int x1, y1, x2, y2;
9371   int life_time = 40;
9372   int element = Tile[ax][ay];
9373   int graphic = el2img(element);
9374   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9375                          level.biomaze);
9376   boolean changed = FALSE;
9377
9378   if (IS_ANIMATED(graphic))
9379     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9380
9381   if (Stop[ax][ay])
9382     return;
9383
9384   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9385     MovDelay[ax][ay] = life_time;
9386
9387   if (MovDelay[ax][ay])         // wait some time before next cycle
9388   {
9389     MovDelay[ax][ay]--;
9390     if (MovDelay[ax][ay])
9391       return;
9392   }
9393
9394   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9395   {
9396     int xx = ax+x1, yy = ay+y1;
9397     int old_element = Tile[xx][yy];
9398     int num_neighbours = 0;
9399
9400     if (!IN_LEV_FIELD(xx, yy))
9401       continue;
9402
9403     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9404     {
9405       int x = xx+x2, y = yy+y2;
9406
9407       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9408         continue;
9409
9410       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9411       boolean is_neighbour = FALSE;
9412
9413       if (level.use_life_bugs)
9414         is_neighbour =
9415           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9416            (IS_FREE(x, y)                             &&  Stop[x][y]));
9417       else
9418         is_neighbour =
9419           (Last[x][y] == element || is_player_cell);
9420
9421       if (is_neighbour)
9422         num_neighbours++;
9423     }
9424
9425     boolean is_free = FALSE;
9426
9427     if (level.use_life_bugs)
9428       is_free = (IS_FREE(xx, yy));
9429     else
9430       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9431
9432     if (xx == ax && yy == ay)           // field in the middle
9433     {
9434       if (num_neighbours < life_parameter[0] ||
9435           num_neighbours > life_parameter[1])
9436       {
9437         Tile[xx][yy] = EL_EMPTY;
9438         if (Tile[xx][yy] != old_element)
9439           TEST_DrawLevelField(xx, yy);
9440         Stop[xx][yy] = TRUE;
9441         changed = TRUE;
9442       }
9443     }
9444     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9445     {                                   // free border field
9446       if (num_neighbours >= life_parameter[2] &&
9447           num_neighbours <= life_parameter[3])
9448       {
9449         Tile[xx][yy] = element;
9450         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time - 1);
9451         if (Tile[xx][yy] != old_element)
9452           TEST_DrawLevelField(xx, yy);
9453         Stop[xx][yy] = TRUE;
9454         changed = TRUE;
9455       }
9456     }
9457   }
9458
9459   if (changed)
9460     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9461                    SND_GAME_OF_LIFE_GROWING);
9462 }
9463
9464 static void InitRobotWheel(int x, int y)
9465 {
9466   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9467 }
9468
9469 static void RunRobotWheel(int x, int y)
9470 {
9471   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9472 }
9473
9474 static void StopRobotWheel(int x, int y)
9475 {
9476   if (game.robot_wheel_x == x &&
9477       game.robot_wheel_y == y)
9478   {
9479     game.robot_wheel_x = -1;
9480     game.robot_wheel_y = -1;
9481     game.robot_wheel_active = FALSE;
9482   }
9483 }
9484
9485 static void InitTimegateWheel(int x, int y)
9486 {
9487   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9488 }
9489
9490 static void RunTimegateWheel(int x, int y)
9491 {
9492   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9493 }
9494
9495 static void InitMagicBallDelay(int x, int y)
9496 {
9497   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9498 }
9499
9500 static void ActivateMagicBall(int bx, int by)
9501 {
9502   int x, y;
9503
9504   if (level.ball_random)
9505   {
9506     int pos_border = RND(8);    // select one of the eight border elements
9507     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9508     int xx = pos_content % 3;
9509     int yy = pos_content / 3;
9510
9511     x = bx - 1 + xx;
9512     y = by - 1 + yy;
9513
9514     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9515       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9516   }
9517   else
9518   {
9519     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9520     {
9521       int xx = x - bx + 1;
9522       int yy = y - by + 1;
9523
9524       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9525         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9526     }
9527   }
9528
9529   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9530 }
9531
9532 static void CheckExit(int x, int y)
9533 {
9534   if (game.gems_still_needed > 0 ||
9535       game.sokoban_fields_still_needed > 0 ||
9536       game.sokoban_objects_still_needed > 0 ||
9537       game.lights_still_needed > 0)
9538   {
9539     int element = Tile[x][y];
9540     int graphic = el2img(element);
9541
9542     if (IS_ANIMATED(graphic))
9543       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9544
9545     return;
9546   }
9547
9548   // do not re-open exit door closed after last player
9549   if (game.all_players_gone)
9550     return;
9551
9552   Tile[x][y] = EL_EXIT_OPENING;
9553
9554   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9555 }
9556
9557 static void CheckExitEM(int x, int y)
9558 {
9559   if (game.gems_still_needed > 0 ||
9560       game.sokoban_fields_still_needed > 0 ||
9561       game.sokoban_objects_still_needed > 0 ||
9562       game.lights_still_needed > 0)
9563   {
9564     int element = Tile[x][y];
9565     int graphic = el2img(element);
9566
9567     if (IS_ANIMATED(graphic))
9568       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9569
9570     return;
9571   }
9572
9573   // do not re-open exit door closed after last player
9574   if (game.all_players_gone)
9575     return;
9576
9577   Tile[x][y] = EL_EM_EXIT_OPENING;
9578
9579   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9580 }
9581
9582 static void CheckExitSteel(int x, int y)
9583 {
9584   if (game.gems_still_needed > 0 ||
9585       game.sokoban_fields_still_needed > 0 ||
9586       game.sokoban_objects_still_needed > 0 ||
9587       game.lights_still_needed > 0)
9588   {
9589     int element = Tile[x][y];
9590     int graphic = el2img(element);
9591
9592     if (IS_ANIMATED(graphic))
9593       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9594
9595     return;
9596   }
9597
9598   // do not re-open exit door closed after last player
9599   if (game.all_players_gone)
9600     return;
9601
9602   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9603
9604   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9605 }
9606
9607 static void CheckExitSteelEM(int x, int y)
9608 {
9609   if (game.gems_still_needed > 0 ||
9610       game.sokoban_fields_still_needed > 0 ||
9611       game.sokoban_objects_still_needed > 0 ||
9612       game.lights_still_needed > 0)
9613   {
9614     int element = Tile[x][y];
9615     int graphic = el2img(element);
9616
9617     if (IS_ANIMATED(graphic))
9618       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9619
9620     return;
9621   }
9622
9623   // do not re-open exit door closed after last player
9624   if (game.all_players_gone)
9625     return;
9626
9627   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9628
9629   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9630 }
9631
9632 static void CheckExitSP(int x, int y)
9633 {
9634   if (game.gems_still_needed > 0)
9635   {
9636     int element = Tile[x][y];
9637     int graphic = el2img(element);
9638
9639     if (IS_ANIMATED(graphic))
9640       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9641
9642     return;
9643   }
9644
9645   // do not re-open exit door closed after last player
9646   if (game.all_players_gone)
9647     return;
9648
9649   Tile[x][y] = EL_SP_EXIT_OPENING;
9650
9651   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9652 }
9653
9654 static void CloseAllOpenTimegates(void)
9655 {
9656   int x, y;
9657
9658   SCAN_PLAYFIELD(x, y)
9659   {
9660     int element = Tile[x][y];
9661
9662     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9663     {
9664       Tile[x][y] = EL_TIMEGATE_CLOSING;
9665
9666       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9667     }
9668   }
9669 }
9670
9671 static void DrawTwinkleOnField(int x, int y)
9672 {
9673   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9674     return;
9675
9676   if (Tile[x][y] == EL_BD_DIAMOND)
9677     return;
9678
9679   if (MovDelay[x][y] == 0)      // next animation frame
9680     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9681
9682   if (MovDelay[x][y] != 0)      // wait some time before next frame
9683   {
9684     MovDelay[x][y]--;
9685
9686     DrawLevelElementAnimation(x, y, Tile[x][y]);
9687
9688     if (MovDelay[x][y] != 0)
9689     {
9690       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9691                                            10 - MovDelay[x][y]);
9692
9693       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9694     }
9695   }
9696 }
9697
9698 static void WallGrowing(int x, int y)
9699 {
9700   int delay = 6;
9701
9702   if (!MovDelay[x][y])          // next animation frame
9703     MovDelay[x][y] = 3 * delay;
9704
9705   if (MovDelay[x][y])           // wait some time before next frame
9706   {
9707     MovDelay[x][y]--;
9708
9709     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9710     {
9711       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9712       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9713
9714       DrawLevelGraphic(x, y, graphic, frame);
9715     }
9716
9717     if (!MovDelay[x][y])
9718     {
9719       if (MovDir[x][y] == MV_LEFT)
9720       {
9721         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9722           TEST_DrawLevelField(x - 1, y);
9723       }
9724       else if (MovDir[x][y] == MV_RIGHT)
9725       {
9726         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9727           TEST_DrawLevelField(x + 1, y);
9728       }
9729       else if (MovDir[x][y] == MV_UP)
9730       {
9731         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9732           TEST_DrawLevelField(x, y - 1);
9733       }
9734       else
9735       {
9736         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9737           TEST_DrawLevelField(x, y + 1);
9738       }
9739
9740       Tile[x][y] = Store[x][y];
9741       Store[x][y] = 0;
9742       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9743       TEST_DrawLevelField(x, y);
9744     }
9745   }
9746 }
9747
9748 static void CheckWallGrowing(int ax, int ay)
9749 {
9750   int element = Tile[ax][ay];
9751   int graphic = el2img(element);
9752   boolean free_top    = FALSE;
9753   boolean free_bottom = FALSE;
9754   boolean free_left   = FALSE;
9755   boolean free_right  = FALSE;
9756   boolean stop_top    = FALSE;
9757   boolean stop_bottom = FALSE;
9758   boolean stop_left   = FALSE;
9759   boolean stop_right  = FALSE;
9760   boolean new_wall    = FALSE;
9761
9762   boolean is_steelwall  = (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9763                            element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9764                            element == EL_EXPANDABLE_STEELWALL_ANY);
9765
9766   boolean grow_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9767                              element == EL_EXPANDABLE_WALL_ANY ||
9768                              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9769                              element == EL_EXPANDABLE_STEELWALL_ANY);
9770
9771   boolean grow_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9772                              element == EL_EXPANDABLE_WALL_ANY ||
9773                              element == EL_EXPANDABLE_WALL ||
9774                              element == EL_BD_EXPANDABLE_WALL ||
9775                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9776                              element == EL_EXPANDABLE_STEELWALL_ANY);
9777
9778   boolean stop_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9779                              element == EL_EXPANDABLE_STEELWALL_VERTICAL);
9780
9781   boolean stop_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9782                              element == EL_EXPANDABLE_WALL ||
9783                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL);
9784
9785   int wall_growing = (is_steelwall ?
9786                       EL_EXPANDABLE_STEELWALL_GROWING :
9787                       EL_EXPANDABLE_WALL_GROWING);
9788
9789   int gfx_wall_growing_up    = (is_steelwall ?
9790                                 IMG_EXPANDABLE_STEELWALL_GROWING_UP :
9791                                 IMG_EXPANDABLE_WALL_GROWING_UP);
9792   int gfx_wall_growing_down  = (is_steelwall ?
9793                                 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN :
9794                                 IMG_EXPANDABLE_WALL_GROWING_DOWN);
9795   int gfx_wall_growing_left  = (is_steelwall ?
9796                                 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT :
9797                                 IMG_EXPANDABLE_WALL_GROWING_LEFT);
9798   int gfx_wall_growing_right = (is_steelwall ?
9799                                 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT :
9800                                 IMG_EXPANDABLE_WALL_GROWING_RIGHT);
9801
9802   if (IS_ANIMATED(graphic))
9803     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9804
9805   if (!MovDelay[ax][ay])        // start building new wall
9806     MovDelay[ax][ay] = 6;
9807
9808   if (MovDelay[ax][ay])         // wait some time before building new wall
9809   {
9810     MovDelay[ax][ay]--;
9811     if (MovDelay[ax][ay])
9812       return;
9813   }
9814
9815   if (IN_LEV_FIELD(ax, ay - 1) && IS_FREE(ax, ay - 1))
9816     free_top = TRUE;
9817   if (IN_LEV_FIELD(ax, ay + 1) && IS_FREE(ax, ay + 1))
9818     free_bottom = TRUE;
9819   if (IN_LEV_FIELD(ax - 1, ay) && IS_FREE(ax - 1, ay))
9820     free_left = TRUE;
9821   if (IN_LEV_FIELD(ax + 1, ay) && IS_FREE(ax + 1, ay))
9822     free_right = TRUE;
9823
9824   if (grow_vertical)
9825   {
9826     if (free_top)
9827     {
9828       Tile[ax][ay - 1] = wall_growing;
9829       Store[ax][ay - 1] = element;
9830       GfxDir[ax][ay - 1] = MovDir[ax][ay - 1] = MV_UP;
9831
9832       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay - 1)))
9833         DrawLevelGraphic(ax, ay - 1, gfx_wall_growing_up, 0);
9834
9835       new_wall = TRUE;
9836     }
9837
9838     if (free_bottom)
9839     {
9840       Tile[ax][ay + 1] = wall_growing;
9841       Store[ax][ay + 1] = element;
9842       GfxDir[ax][ay + 1] = MovDir[ax][ay + 1] = MV_DOWN;
9843
9844       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay + 1)))
9845         DrawLevelGraphic(ax, ay + 1, gfx_wall_growing_down, 0);
9846
9847       new_wall = TRUE;
9848     }
9849   }
9850
9851   if (grow_horizontal)
9852   {
9853     if (free_left)
9854     {
9855       Tile[ax - 1][ay] = wall_growing;
9856       Store[ax - 1][ay] = element;
9857       GfxDir[ax - 1][ay] = MovDir[ax - 1][ay] = MV_LEFT;
9858
9859       if (IN_SCR_FIELD(SCREENX(ax - 1), SCREENY(ay)))
9860         DrawLevelGraphic(ax - 1, ay, gfx_wall_growing_left, 0);
9861
9862       new_wall = TRUE;
9863     }
9864
9865     if (free_right)
9866     {
9867       Tile[ax + 1][ay] = wall_growing;
9868       Store[ax + 1][ay] = element;
9869       GfxDir[ax + 1][ay] = MovDir[ax + 1][ay] = MV_RIGHT;
9870
9871       if (IN_SCR_FIELD(SCREENX(ax + 1), SCREENY(ay)))
9872         DrawLevelGraphic(ax + 1, ay, gfx_wall_growing_right, 0);
9873
9874       new_wall = TRUE;
9875     }
9876   }
9877
9878   if (element == EL_EXPANDABLE_WALL && (free_left || free_right))
9879     TEST_DrawLevelField(ax, ay);
9880
9881   if (!IN_LEV_FIELD(ax, ay - 1) || IS_WALL(Tile[ax][ay - 1]))
9882     stop_top = TRUE;
9883   if (!IN_LEV_FIELD(ax, ay + 1) || IS_WALL(Tile[ax][ay + 1]))
9884     stop_bottom = TRUE;
9885   if (!IN_LEV_FIELD(ax - 1, ay) || IS_WALL(Tile[ax - 1][ay]))
9886     stop_left = TRUE;
9887   if (!IN_LEV_FIELD(ax + 1, ay) || IS_WALL(Tile[ax + 1][ay]))
9888     stop_right = TRUE;
9889
9890   if (((stop_top && stop_bottom) || stop_horizontal) &&
9891       ((stop_left && stop_right) || stop_vertical))
9892     Tile[ax][ay] = (is_steelwall ? EL_STEELWALL : EL_WALL);
9893
9894   if (new_wall)
9895     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9896 }
9897
9898 static void CheckForDragon(int x, int y)
9899 {
9900   int i, j;
9901   boolean dragon_found = FALSE;
9902   struct XY *xy = xy_topdown;
9903
9904   for (i = 0; i < NUM_DIRECTIONS; i++)
9905   {
9906     for (j = 0; j < 4; j++)
9907     {
9908       int xx = x + j * xy[i].x;
9909       int yy = y + j * xy[i].y;
9910
9911       if (IN_LEV_FIELD(xx, yy) &&
9912           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9913       {
9914         if (Tile[xx][yy] == EL_DRAGON)
9915           dragon_found = TRUE;
9916       }
9917       else
9918         break;
9919     }
9920   }
9921
9922   if (!dragon_found)
9923   {
9924     for (i = 0; i < NUM_DIRECTIONS; i++)
9925     {
9926       for (j = 0; j < 3; j++)
9927       {
9928         int xx = x + j * xy[i].x;
9929         int yy = y + j * xy[i].y;
9930
9931         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9932         {
9933           Tile[xx][yy] = EL_EMPTY;
9934           TEST_DrawLevelField(xx, yy);
9935         }
9936         else
9937           break;
9938       }
9939     }
9940   }
9941 }
9942
9943 static void InitBuggyBase(int x, int y)
9944 {
9945   int element = Tile[x][y];
9946   int activating_delay = FRAMES_PER_SECOND / 4;
9947
9948   ChangeDelay[x][y] =
9949     (element == EL_SP_BUGGY_BASE ?
9950      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9951      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9952      activating_delay :
9953      element == EL_SP_BUGGY_BASE_ACTIVE ?
9954      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9955 }
9956
9957 static void WarnBuggyBase(int x, int y)
9958 {
9959   int i;
9960   struct XY *xy = xy_topdown;
9961
9962   for (i = 0; i < NUM_DIRECTIONS; i++)
9963   {
9964     int xx = x + xy[i].x;
9965     int yy = y + xy[i].y;
9966
9967     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9968     {
9969       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9970
9971       break;
9972     }
9973   }
9974 }
9975
9976 static void InitTrap(int x, int y)
9977 {
9978   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9979 }
9980
9981 static void ActivateTrap(int x, int y)
9982 {
9983   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9984 }
9985
9986 static void ChangeActiveTrap(int x, int y)
9987 {
9988   int graphic = IMG_TRAP_ACTIVE;
9989
9990   // if new animation frame was drawn, correct crumbled sand border
9991   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9992     TEST_DrawLevelFieldCrumbled(x, y);
9993 }
9994
9995 static int getSpecialActionElement(int element, int number, int base_element)
9996 {
9997   return (element != EL_EMPTY ? element :
9998           number != -1 ? base_element + number - 1 :
9999           EL_EMPTY);
10000 }
10001
10002 static int getModifiedActionNumber(int value_old, int operator, int operand,
10003                                    int value_min, int value_max)
10004 {
10005   int value_new = (operator == CA_MODE_SET      ? operand :
10006                    operator == CA_MODE_ADD      ? value_old + operand :
10007                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10008                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10009                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10010                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10011                    value_old);
10012
10013   return (value_new < value_min ? value_min :
10014           value_new > value_max ? value_max :
10015           value_new);
10016 }
10017
10018 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10019 {
10020   struct ElementInfo *ei = &element_info[element];
10021   struct ElementChangeInfo *change = &ei->change_page[page];
10022   int target_element = change->target_element;
10023   int action_type = change->action_type;
10024   int action_mode = change->action_mode;
10025   int action_arg = change->action_arg;
10026   int action_element = change->action_element;
10027   int i;
10028
10029   if (!change->has_action)
10030     return;
10031
10032   // ---------- determine action paramater values -----------------------------
10033
10034   int level_time_value =
10035     (level.time > 0 ? TimeLeft :
10036      TimePlayed);
10037
10038   int action_arg_element_raw =
10039     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10040      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10041      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10042      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10043      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10044      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10045      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10046      EL_EMPTY);
10047   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10048
10049   int action_arg_direction =
10050     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10051      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10052      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10053      change->actual_trigger_side :
10054      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10055      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10056      MV_NONE);
10057
10058   int action_arg_number_min =
10059     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10060      CA_ARG_MIN);
10061
10062   int action_arg_number_max =
10063     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10064      action_type == CA_SET_LEVEL_GEMS ? 999 :
10065      action_type == CA_SET_LEVEL_TIME ? 9999 :
10066      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10067      action_type == CA_SET_CE_VALUE ? 9999 :
10068      action_type == CA_SET_CE_SCORE ? 9999 :
10069      CA_ARG_MAX);
10070
10071   int action_arg_number_reset =
10072     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10073      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10074      action_type == CA_SET_LEVEL_TIME ? level.time :
10075      action_type == CA_SET_LEVEL_SCORE ? 0 :
10076      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10077      action_type == CA_SET_CE_SCORE ? 0 :
10078      0);
10079
10080   int action_arg_number =
10081     (action_arg <= CA_ARG_MAX ? action_arg :
10082      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10083      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10084      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10085      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10086      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10087      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10088      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10089      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10090      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10091      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10092      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10093      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10094      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10095      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10096      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10097      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10098      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10099      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10100      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10101      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10102      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10103      -1);
10104
10105   int action_arg_number_old =
10106     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10107      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10108      action_type == CA_SET_LEVEL_SCORE ? game.score :
10109      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10110      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10111      0);
10112
10113   int action_arg_number_new =
10114     getModifiedActionNumber(action_arg_number_old,
10115                             action_mode, action_arg_number,
10116                             action_arg_number_min, action_arg_number_max);
10117
10118   int trigger_player_bits =
10119     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10120      change->actual_trigger_player_bits : change->trigger_player);
10121
10122   int action_arg_player_bits =
10123     (action_arg >= CA_ARG_PLAYER_1 &&
10124      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10125      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10126      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10127      PLAYER_BITS_ANY);
10128
10129   // ---------- execute action  -----------------------------------------------
10130
10131   switch (action_type)
10132   {
10133     case CA_NO_ACTION:
10134     {
10135       return;
10136     }
10137
10138     // ---------- level actions  ----------------------------------------------
10139
10140     case CA_RESTART_LEVEL:
10141     {
10142       game.restart_level = TRUE;
10143
10144       break;
10145     }
10146
10147     case CA_SHOW_ENVELOPE:
10148     {
10149       int element = getSpecialActionElement(action_arg_element,
10150                                             action_arg_number, EL_ENVELOPE_1);
10151
10152       if (IS_ENVELOPE(element))
10153         local_player->show_envelope = element;
10154
10155       break;
10156     }
10157
10158     case CA_SET_LEVEL_TIME:
10159     {
10160       if (level.time > 0)       // only modify limited time value
10161       {
10162         TimeLeft = action_arg_number_new;
10163
10164         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10165
10166         DisplayGameControlValues();
10167
10168         if (!TimeLeft && game.time_limit)
10169           for (i = 0; i < MAX_PLAYERS; i++)
10170             KillPlayer(&stored_player[i]);
10171       }
10172
10173       break;
10174     }
10175
10176     case CA_SET_LEVEL_SCORE:
10177     {
10178       game.score = action_arg_number_new;
10179
10180       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10181
10182       DisplayGameControlValues();
10183
10184       break;
10185     }
10186
10187     case CA_SET_LEVEL_GEMS:
10188     {
10189       game.gems_still_needed = action_arg_number_new;
10190
10191       game.snapshot.collected_item = TRUE;
10192
10193       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10194
10195       DisplayGameControlValues();
10196
10197       break;
10198     }
10199
10200     case CA_SET_LEVEL_WIND:
10201     {
10202       game.wind_direction = action_arg_direction;
10203
10204       break;
10205     }
10206
10207     case CA_SET_LEVEL_RANDOM_SEED:
10208     {
10209       // ensure that setting a new random seed while playing is predictable
10210       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10211
10212       break;
10213     }
10214
10215     // ---------- player actions  ---------------------------------------------
10216
10217     case CA_MOVE_PLAYER:
10218     case CA_MOVE_PLAYER_NEW:
10219     {
10220       // automatically move to the next field in specified direction
10221       for (i = 0; i < MAX_PLAYERS; i++)
10222         if (trigger_player_bits & (1 << i))
10223           if (action_type == CA_MOVE_PLAYER ||
10224               stored_player[i].MovPos == 0)
10225             stored_player[i].programmed_action = action_arg_direction;
10226
10227       break;
10228     }
10229
10230     case CA_EXIT_PLAYER:
10231     {
10232       for (i = 0; i < MAX_PLAYERS; i++)
10233         if (action_arg_player_bits & (1 << i))
10234           ExitPlayer(&stored_player[i]);
10235
10236       if (game.players_still_needed == 0)
10237         LevelSolved();
10238
10239       break;
10240     }
10241
10242     case CA_KILL_PLAYER:
10243     {
10244       for (i = 0; i < MAX_PLAYERS; i++)
10245         if (action_arg_player_bits & (1 << i))
10246           KillPlayer(&stored_player[i]);
10247
10248       break;
10249     }
10250
10251     case CA_SET_PLAYER_KEYS:
10252     {
10253       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10254       int element = getSpecialActionElement(action_arg_element,
10255                                             action_arg_number, EL_KEY_1);
10256
10257       if (IS_KEY(element))
10258       {
10259         for (i = 0; i < MAX_PLAYERS; i++)
10260         {
10261           if (trigger_player_bits & (1 << i))
10262           {
10263             stored_player[i].key[KEY_NR(element)] = key_state;
10264
10265             DrawGameDoorValues();
10266           }
10267         }
10268       }
10269
10270       break;
10271     }
10272
10273     case CA_SET_PLAYER_SPEED:
10274     {
10275       for (i = 0; i < MAX_PLAYERS; i++)
10276       {
10277         if (trigger_player_bits & (1 << i))
10278         {
10279           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10280
10281           if (action_arg == CA_ARG_SPEED_FASTER &&
10282               stored_player[i].cannot_move)
10283           {
10284             action_arg_number = STEPSIZE_VERY_SLOW;
10285           }
10286           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10287                    action_arg == CA_ARG_SPEED_FASTER)
10288           {
10289             action_arg_number = 2;
10290             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10291                            CA_MODE_MULTIPLY);
10292           }
10293           else if (action_arg == CA_ARG_NUMBER_RESET)
10294           {
10295             action_arg_number = level.initial_player_stepsize[i];
10296           }
10297
10298           move_stepsize =
10299             getModifiedActionNumber(move_stepsize,
10300                                     action_mode,
10301                                     action_arg_number,
10302                                     action_arg_number_min,
10303                                     action_arg_number_max);
10304
10305           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10306         }
10307       }
10308
10309       break;
10310     }
10311
10312     case CA_SET_PLAYER_SHIELD:
10313     {
10314       for (i = 0; i < MAX_PLAYERS; i++)
10315       {
10316         if (trigger_player_bits & (1 << i))
10317         {
10318           if (action_arg == CA_ARG_SHIELD_OFF)
10319           {
10320             stored_player[i].shield_normal_time_left = 0;
10321             stored_player[i].shield_deadly_time_left = 0;
10322           }
10323           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10324           {
10325             stored_player[i].shield_normal_time_left = 999999;
10326           }
10327           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10328           {
10329             stored_player[i].shield_normal_time_left = 999999;
10330             stored_player[i].shield_deadly_time_left = 999999;
10331           }
10332         }
10333       }
10334
10335       break;
10336     }
10337
10338     case CA_SET_PLAYER_GRAVITY:
10339     {
10340       for (i = 0; i < MAX_PLAYERS; i++)
10341       {
10342         if (trigger_player_bits & (1 << i))
10343         {
10344           stored_player[i].gravity =
10345             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10346              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10347              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10348              stored_player[i].gravity);
10349         }
10350       }
10351
10352       break;
10353     }
10354
10355     case CA_SET_PLAYER_ARTWORK:
10356     {
10357       for (i = 0; i < MAX_PLAYERS; i++)
10358       {
10359         if (trigger_player_bits & (1 << i))
10360         {
10361           int artwork_element = action_arg_element;
10362
10363           if (action_arg == CA_ARG_ELEMENT_RESET)
10364             artwork_element =
10365               (level.use_artwork_element[i] ? level.artwork_element[i] :
10366                stored_player[i].element_nr);
10367
10368           if (stored_player[i].artwork_element != artwork_element)
10369             stored_player[i].Frame = 0;
10370
10371           stored_player[i].artwork_element = artwork_element;
10372
10373           SetPlayerWaiting(&stored_player[i], FALSE);
10374
10375           // set number of special actions for bored and sleeping animation
10376           stored_player[i].num_special_action_bored =
10377             get_num_special_action(artwork_element,
10378                                    ACTION_BORING_1, ACTION_BORING_LAST);
10379           stored_player[i].num_special_action_sleeping =
10380             get_num_special_action(artwork_element,
10381                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10382         }
10383       }
10384
10385       break;
10386     }
10387
10388     case CA_SET_PLAYER_INVENTORY:
10389     {
10390       for (i = 0; i < MAX_PLAYERS; i++)
10391       {
10392         struct PlayerInfo *player = &stored_player[i];
10393         int j, k;
10394
10395         if (trigger_player_bits & (1 << i))
10396         {
10397           int inventory_element = action_arg_element;
10398
10399           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10400               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10401               action_arg == CA_ARG_ELEMENT_ACTION)
10402           {
10403             int element = inventory_element;
10404             int collect_count = element_info[element].collect_count_initial;
10405
10406             if (!IS_CUSTOM_ELEMENT(element))
10407               collect_count = 1;
10408
10409             if (collect_count == 0)
10410               player->inventory_infinite_element = element;
10411             else
10412               for (k = 0; k < collect_count; k++)
10413                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10414                   player->inventory_element[player->inventory_size++] =
10415                     element;
10416           }
10417           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10418                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10419                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10420           {
10421             if (player->inventory_infinite_element != EL_UNDEFINED &&
10422                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10423                                      action_arg_element_raw))
10424               player->inventory_infinite_element = EL_UNDEFINED;
10425
10426             for (k = 0, j = 0; j < player->inventory_size; j++)
10427             {
10428               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10429                                         action_arg_element_raw))
10430                 player->inventory_element[k++] = player->inventory_element[j];
10431             }
10432
10433             player->inventory_size = k;
10434           }
10435           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10436           {
10437             if (player->inventory_size > 0)
10438             {
10439               for (j = 0; j < player->inventory_size - 1; j++)
10440                 player->inventory_element[j] = player->inventory_element[j + 1];
10441
10442               player->inventory_size--;
10443             }
10444           }
10445           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10446           {
10447             if (player->inventory_size > 0)
10448               player->inventory_size--;
10449           }
10450           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10451           {
10452             player->inventory_infinite_element = EL_UNDEFINED;
10453             player->inventory_size = 0;
10454           }
10455           else if (action_arg == CA_ARG_INVENTORY_RESET)
10456           {
10457             player->inventory_infinite_element = EL_UNDEFINED;
10458             player->inventory_size = 0;
10459
10460             if (level.use_initial_inventory[i])
10461             {
10462               for (j = 0; j < level.initial_inventory_size[i]; j++)
10463               {
10464                 int element = level.initial_inventory_content[i][j];
10465                 int collect_count = element_info[element].collect_count_initial;
10466
10467                 if (!IS_CUSTOM_ELEMENT(element))
10468                   collect_count = 1;
10469
10470                 if (collect_count == 0)
10471                   player->inventory_infinite_element = element;
10472                 else
10473                   for (k = 0; k < collect_count; k++)
10474                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10475                       player->inventory_element[player->inventory_size++] =
10476                         element;
10477               }
10478             }
10479           }
10480         }
10481       }
10482
10483       break;
10484     }
10485
10486     // ---------- CE actions  -------------------------------------------------
10487
10488     case CA_SET_CE_VALUE:
10489     {
10490       int last_ce_value = CustomValue[x][y];
10491
10492       CustomValue[x][y] = action_arg_number_new;
10493
10494       if (CustomValue[x][y] != last_ce_value)
10495       {
10496         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10497         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10498
10499         if (CustomValue[x][y] == 0)
10500         {
10501           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10502           ChangeCount[x][y] = 0;        // allow at least one more change
10503
10504           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10505           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10506         }
10507       }
10508
10509       break;
10510     }
10511
10512     case CA_SET_CE_SCORE:
10513     {
10514       int last_ce_score = ei->collect_score;
10515
10516       ei->collect_score = action_arg_number_new;
10517
10518       if (ei->collect_score != last_ce_score)
10519       {
10520         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10521         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10522
10523         if (ei->collect_score == 0)
10524         {
10525           int xx, yy;
10526
10527           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10528           ChangeCount[x][y] = 0;        // allow at least one more change
10529
10530           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10531           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10532
10533           /*
10534             This is a very special case that seems to be a mixture between
10535             CheckElementChange() and CheckTriggeredElementChange(): while
10536             the first one only affects single elements that are triggered
10537             directly, the second one affects multiple elements in the playfield
10538             that are triggered indirectly by another element. This is a third
10539             case: Changing the CE score always affects multiple identical CEs,
10540             so every affected CE must be checked, not only the single CE for
10541             which the CE score was changed in the first place (as every instance
10542             of that CE shares the same CE score, and therefore also can change)!
10543           */
10544           SCAN_PLAYFIELD(xx, yy)
10545           {
10546             if (Tile[xx][yy] == element)
10547               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10548                                  CE_SCORE_GETS_ZERO);
10549           }
10550         }
10551       }
10552
10553       break;
10554     }
10555
10556     case CA_SET_CE_ARTWORK:
10557     {
10558       int artwork_element = action_arg_element;
10559       boolean reset_frame = FALSE;
10560       int xx, yy;
10561
10562       if (action_arg == CA_ARG_ELEMENT_RESET)
10563         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10564                            element);
10565
10566       if (ei->gfx_element != artwork_element)
10567         reset_frame = TRUE;
10568
10569       ei->gfx_element = artwork_element;
10570
10571       SCAN_PLAYFIELD(xx, yy)
10572       {
10573         if (Tile[xx][yy] == element)
10574         {
10575           if (reset_frame)
10576           {
10577             ResetGfxAnimation(xx, yy);
10578             ResetRandomAnimationValue(xx, yy);
10579           }
10580
10581           TEST_DrawLevelField(xx, yy);
10582         }
10583       }
10584
10585       break;
10586     }
10587
10588     // ---------- engine actions  ---------------------------------------------
10589
10590     case CA_SET_ENGINE_SCAN_MODE:
10591     {
10592       InitPlayfieldScanMode(action_arg);
10593
10594       break;
10595     }
10596
10597     default:
10598       break;
10599   }
10600 }
10601
10602 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10603 {
10604   int old_element = Tile[x][y];
10605   int new_element = GetElementFromGroupElement(element);
10606   int previous_move_direction = MovDir[x][y];
10607   int last_ce_value = CustomValue[x][y];
10608   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10609   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10610   boolean add_player_onto_element = (new_element_is_player &&
10611                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10612                                      IS_WALKABLE(old_element));
10613
10614   if (!add_player_onto_element)
10615   {
10616     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10617       RemoveMovingField(x, y);
10618     else
10619       RemoveField(x, y);
10620
10621     Tile[x][y] = new_element;
10622
10623     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10624       MovDir[x][y] = previous_move_direction;
10625
10626     if (element_info[new_element].use_last_ce_value)
10627       CustomValue[x][y] = last_ce_value;
10628
10629     InitField_WithBug1(x, y, FALSE);
10630
10631     new_element = Tile[x][y];   // element may have changed
10632
10633     ResetGfxAnimation(x, y);
10634     ResetRandomAnimationValue(x, y);
10635
10636     TEST_DrawLevelField(x, y);
10637
10638     if (GFX_CRUMBLED(new_element))
10639       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10640   }
10641
10642   // check if element under the player changes from accessible to unaccessible
10643   // (needed for special case of dropping element which then changes)
10644   // (must be checked after creating new element for walkable group elements)
10645   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10646       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10647   {
10648     Bang(x, y);
10649
10650     return;
10651   }
10652
10653   // "ChangeCount" not set yet to allow "entered by player" change one time
10654   if (new_element_is_player)
10655     RelocatePlayer(x, y, new_element);
10656
10657   if (is_change)
10658     ChangeCount[x][y]++;        // count number of changes in the same frame
10659
10660   TestIfBadThingTouchesPlayer(x, y);
10661   TestIfPlayerTouchesCustomElement(x, y);
10662   TestIfElementTouchesCustomElement(x, y);
10663 }
10664
10665 static void CreateField(int x, int y, int element)
10666 {
10667   CreateFieldExt(x, y, element, FALSE);
10668 }
10669
10670 static void CreateElementFromChange(int x, int y, int element)
10671 {
10672   element = GET_VALID_RUNTIME_ELEMENT(element);
10673
10674   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10675   {
10676     int old_element = Tile[x][y];
10677
10678     // prevent changed element from moving in same engine frame
10679     // unless both old and new element can either fall or move
10680     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10681         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10682       Stop[x][y] = TRUE;
10683   }
10684
10685   CreateFieldExt(x, y, element, TRUE);
10686 }
10687
10688 static boolean ChangeElement(int x, int y, int element, int page)
10689 {
10690   struct ElementInfo *ei = &element_info[element];
10691   struct ElementChangeInfo *change = &ei->change_page[page];
10692   int ce_value = CustomValue[x][y];
10693   int ce_score = ei->collect_score;
10694   int target_element;
10695   int old_element = Tile[x][y];
10696
10697   // always use default change event to prevent running into a loop
10698   if (ChangeEvent[x][y] == -1)
10699     ChangeEvent[x][y] = CE_DELAY;
10700
10701   if (ChangeEvent[x][y] == CE_DELAY)
10702   {
10703     // reset actual trigger element, trigger player and action element
10704     change->actual_trigger_element = EL_EMPTY;
10705     change->actual_trigger_player = EL_EMPTY;
10706     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10707     change->actual_trigger_side = CH_SIDE_NONE;
10708     change->actual_trigger_ce_value = 0;
10709     change->actual_trigger_ce_score = 0;
10710   }
10711
10712   // do not change elements more than a specified maximum number of changes
10713   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10714     return FALSE;
10715
10716   ChangeCount[x][y]++;          // count number of changes in the same frame
10717
10718   if (ei->has_anim_event)
10719     HandleGlobalAnimEventByElementChange(element, page, x, y);
10720
10721   if (change->explode)
10722   {
10723     Bang(x, y);
10724
10725     return TRUE;
10726   }
10727
10728   if (change->use_target_content)
10729   {
10730     boolean complete_replace = TRUE;
10731     boolean can_replace[3][3];
10732     int xx, yy;
10733
10734     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10735     {
10736       boolean is_empty;
10737       boolean is_walkable;
10738       boolean is_diggable;
10739       boolean is_collectible;
10740       boolean is_removable;
10741       boolean is_destructible;
10742       int ex = x + xx - 1;
10743       int ey = y + yy - 1;
10744       int content_element = change->target_content.e[xx][yy];
10745       int e;
10746
10747       can_replace[xx][yy] = TRUE;
10748
10749       if (ex == x && ey == y)   // do not check changing element itself
10750         continue;
10751
10752       if (content_element == EL_EMPTY_SPACE)
10753       {
10754         can_replace[xx][yy] = FALSE;    // do not replace border with space
10755
10756         continue;
10757       }
10758
10759       if (!IN_LEV_FIELD(ex, ey))
10760       {
10761         can_replace[xx][yy] = FALSE;
10762         complete_replace = FALSE;
10763
10764         continue;
10765       }
10766
10767       e = Tile[ex][ey];
10768
10769       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10770         e = MovingOrBlocked2Element(ex, ey);
10771
10772       is_empty = (IS_FREE(ex, ey) ||
10773                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10774
10775       is_walkable     = (is_empty || IS_WALKABLE(e));
10776       is_diggable     = (is_empty || IS_DIGGABLE(e));
10777       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10778       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10779       is_removable    = (is_diggable || is_collectible);
10780
10781       can_replace[xx][yy] =
10782         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10783           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10784           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10785           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10786           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10787           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10788          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10789
10790       if (!can_replace[xx][yy])
10791         complete_replace = FALSE;
10792     }
10793
10794     if (!change->only_if_complete || complete_replace)
10795     {
10796       boolean something_has_changed = FALSE;
10797
10798       if (change->only_if_complete && change->use_random_replace &&
10799           RND(100) < change->random_percentage)
10800         return FALSE;
10801
10802       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10803       {
10804         int ex = x + xx - 1;
10805         int ey = y + yy - 1;
10806         int content_element;
10807
10808         if (can_replace[xx][yy] && (!change->use_random_replace ||
10809                                     RND(100) < change->random_percentage))
10810         {
10811           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10812             RemoveMovingField(ex, ey);
10813
10814           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10815
10816           content_element = change->target_content.e[xx][yy];
10817           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10818                                               ce_value, ce_score);
10819
10820           CreateElementFromChange(ex, ey, target_element);
10821
10822           something_has_changed = TRUE;
10823
10824           // for symmetry reasons, freeze newly created border elements
10825           if (ex != x || ey != y)
10826             Stop[ex][ey] = TRUE;        // no more moving in this frame
10827         }
10828       }
10829
10830       if (something_has_changed)
10831       {
10832         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10833         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10834       }
10835     }
10836   }
10837   else
10838   {
10839     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10840                                         ce_value, ce_score);
10841
10842     if (element == EL_DIAGONAL_GROWING ||
10843         element == EL_DIAGONAL_SHRINKING)
10844     {
10845       target_element = Store[x][y];
10846
10847       Store[x][y] = EL_EMPTY;
10848     }
10849
10850     // special case: element changes to player (and may be kept if walkable)
10851     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
10852       CreateElementFromChange(x, y, EL_EMPTY);
10853
10854     CreateElementFromChange(x, y, target_element);
10855
10856     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10857     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10858   }
10859
10860   // this uses direct change before indirect change
10861   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10862
10863   return TRUE;
10864 }
10865
10866 static void HandleElementChange(int x, int y, int page)
10867 {
10868   int element = MovingOrBlocked2Element(x, y);
10869   struct ElementInfo *ei = &element_info[element];
10870   struct ElementChangeInfo *change = &ei->change_page[page];
10871   boolean handle_action_before_change = FALSE;
10872
10873 #ifdef DEBUG
10874   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10875       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10876   {
10877     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10878           x, y, element, element_info[element].token_name);
10879     Debug("game:playing:HandleElementChange", "This should never happen!");
10880   }
10881 #endif
10882
10883   // this can happen with classic bombs on walkable, changing elements
10884   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10885   {
10886     return;
10887   }
10888
10889   if (ChangeDelay[x][y] == 0)           // initialize element change
10890   {
10891     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10892
10893     if (change->can_change)
10894     {
10895       // !!! not clear why graphic animation should be reset at all here !!!
10896       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10897       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10898
10899       /*
10900         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10901
10902         When using an animation frame delay of 1 (this only happens with
10903         "sp_zonk.moving.left/right" in the classic graphics), the default
10904         (non-moving) animation shows wrong animation frames (while the
10905         moving animation, like "sp_zonk.moving.left/right", is correct,
10906         so this graphical bug never shows up with the classic graphics).
10907         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10908         be drawn instead of the correct frames 0,1,2,3. This is caused by
10909         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10910         an element change: First when the change delay ("ChangeDelay[][]")
10911         counter has reached zero after decrementing, then a second time in
10912         the next frame (after "GfxFrame[][]" was already incremented) when
10913         "ChangeDelay[][]" is reset to the initial delay value again.
10914
10915         This causes frame 0 to be drawn twice, while the last frame won't
10916         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10917
10918         As some animations may already be cleverly designed around this bug
10919         (at least the "Snake Bite" snake tail animation does this), it cannot
10920         simply be fixed here without breaking such existing animations.
10921         Unfortunately, it cannot easily be detected if a graphics set was
10922         designed "before" or "after" the bug was fixed. As a workaround,
10923         a new graphics set option "game.graphics_engine_version" was added
10924         to be able to specify the game's major release version for which the
10925         graphics set was designed, which can then be used to decide if the
10926         bugfix should be used (version 4 and above) or not (version 3 or
10927         below, or if no version was specified at all, as with old sets).
10928
10929         (The wrong/fixed animation frames can be tested with the test level set
10930         "test_gfxframe" and level "000", which contains a specially prepared
10931         custom element at level position (x/y) == (11/9) which uses the zonk
10932         animation mentioned above. Using "game.graphics_engine_version: 4"
10933         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10934         This can also be seen from the debug output for this test element.)
10935       */
10936
10937       // when a custom element is about to change (for example by change delay),
10938       // do not reset graphic animation when the custom element is moving
10939       if (game.graphics_engine_version < 4 &&
10940           !IS_MOVING(x, y))
10941       {
10942         ResetGfxAnimation(x, y);
10943         ResetRandomAnimationValue(x, y);
10944       }
10945
10946       if (change->pre_change_function)
10947         change->pre_change_function(x, y);
10948     }
10949   }
10950
10951   ChangeDelay[x][y]--;
10952
10953   if (ChangeDelay[x][y] != 0)           // continue element change
10954   {
10955     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10956
10957     // also needed if CE can not change, but has CE delay with CE action
10958     if (IS_ANIMATED(graphic))
10959       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10960
10961     if (change->can_change)
10962     {
10963       if (change->change_function)
10964         change->change_function(x, y);
10965     }
10966   }
10967   else                                  // finish element change
10968   {
10969     if (ChangePage[x][y] != -1)         // remember page from delayed change
10970     {
10971       page = ChangePage[x][y];
10972       ChangePage[x][y] = -1;
10973
10974       change = &ei->change_page[page];
10975     }
10976
10977     if (IS_MOVING(x, y))                // never change a running system ;-)
10978     {
10979       ChangeDelay[x][y] = 1;            // try change after next move step
10980       ChangePage[x][y] = page;          // remember page to use for change
10981
10982       return;
10983     }
10984
10985     // special case: set new level random seed before changing element
10986     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10987       handle_action_before_change = TRUE;
10988
10989     if (change->has_action && handle_action_before_change)
10990       ExecuteCustomElementAction(x, y, element, page);
10991
10992     if (change->can_change)
10993     {
10994       if (ChangeElement(x, y, element, page))
10995       {
10996         if (change->post_change_function)
10997           change->post_change_function(x, y);
10998       }
10999     }
11000
11001     if (change->has_action && !handle_action_before_change)
11002       ExecuteCustomElementAction(x, y, element, page);
11003   }
11004 }
11005
11006 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11007                                               int trigger_element,
11008                                               int trigger_event,
11009                                               int trigger_player,
11010                                               int trigger_side,
11011                                               int trigger_page)
11012 {
11013   boolean change_done_any = FALSE;
11014   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11015   int i;
11016
11017   if (!(trigger_events[trigger_element][trigger_event]))
11018     return FALSE;
11019
11020   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11021
11022   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11023   {
11024     int element = EL_CUSTOM_START + i;
11025     boolean change_done = FALSE;
11026     int p;
11027
11028     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11029         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11030       continue;
11031
11032     for (p = 0; p < element_info[element].num_change_pages; p++)
11033     {
11034       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11035
11036       if (change->can_change_or_has_action &&
11037           change->has_event[trigger_event] &&
11038           change->trigger_side & trigger_side &&
11039           change->trigger_player & trigger_player &&
11040           change->trigger_page & trigger_page_bits &&
11041           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11042       {
11043         change->actual_trigger_element = trigger_element;
11044         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11045         change->actual_trigger_player_bits = trigger_player;
11046         change->actual_trigger_side = trigger_side;
11047         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11048         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11049
11050         if ((change->can_change && !change_done) || change->has_action)
11051         {
11052           int x, y;
11053
11054           SCAN_PLAYFIELD(x, y)
11055           {
11056             if (Tile[x][y] == element)
11057             {
11058               if (change->can_change && !change_done)
11059               {
11060                 // if element already changed in this frame, not only prevent
11061                 // another element change (checked in ChangeElement()), but
11062                 // also prevent additional element actions for this element
11063
11064                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11065                     !level.use_action_after_change_bug)
11066                   continue;
11067
11068                 ChangeDelay[x][y] = 1;
11069                 ChangeEvent[x][y] = trigger_event;
11070
11071                 HandleElementChange(x, y, p);
11072               }
11073               else if (change->has_action)
11074               {
11075                 // if element already changed in this frame, not only prevent
11076                 // another element change (checked in ChangeElement()), but
11077                 // also prevent additional element actions for this element
11078
11079                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11080                     !level.use_action_after_change_bug)
11081                   continue;
11082
11083                 ExecuteCustomElementAction(x, y, element, p);
11084                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11085               }
11086             }
11087           }
11088
11089           if (change->can_change)
11090           {
11091             change_done = TRUE;
11092             change_done_any = TRUE;
11093           }
11094         }
11095       }
11096     }
11097   }
11098
11099   RECURSION_LOOP_DETECTION_END();
11100
11101   return change_done_any;
11102 }
11103
11104 static boolean CheckElementChangeExt(int x, int y,
11105                                      int element,
11106                                      int trigger_element,
11107                                      int trigger_event,
11108                                      int trigger_player,
11109                                      int trigger_side)
11110 {
11111   boolean change_done = FALSE;
11112   int p;
11113
11114   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11115       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11116     return FALSE;
11117
11118   if (Tile[x][y] == EL_BLOCKED)
11119   {
11120     Blocked2Moving(x, y, &x, &y);
11121     element = Tile[x][y];
11122   }
11123
11124   // check if element has already changed or is about to change after moving
11125   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11126        Tile[x][y] != element) ||
11127
11128       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11129        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11130         ChangePage[x][y] != -1)))
11131     return FALSE;
11132
11133   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11134
11135   for (p = 0; p < element_info[element].num_change_pages; p++)
11136   {
11137     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11138
11139     /* check trigger element for all events where the element that is checked
11140        for changing interacts with a directly adjacent element -- this is
11141        different to element changes that affect other elements to change on the
11142        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11143     boolean check_trigger_element =
11144       (trigger_event == CE_NEXT_TO_X ||
11145        trigger_event == CE_TOUCHING_X ||
11146        trigger_event == CE_HITTING_X ||
11147        trigger_event == CE_HIT_BY_X ||
11148        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11149
11150     if (change->can_change_or_has_action &&
11151         change->has_event[trigger_event] &&
11152         change->trigger_side & trigger_side &&
11153         change->trigger_player & trigger_player &&
11154         (!check_trigger_element ||
11155          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11156     {
11157       change->actual_trigger_element = trigger_element;
11158       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11159       change->actual_trigger_player_bits = trigger_player;
11160       change->actual_trigger_side = trigger_side;
11161       change->actual_trigger_ce_value = CustomValue[x][y];
11162       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11163
11164       // special case: trigger element not at (x,y) position for some events
11165       if (check_trigger_element)
11166       {
11167         static struct
11168         {
11169           int dx, dy;
11170         } move_xy[] =
11171           {
11172             {  0,  0 },
11173             { -1,  0 },
11174             { +1,  0 },
11175             {  0,  0 },
11176             {  0, -1 },
11177             {  0,  0 }, { 0, 0 }, { 0, 0 },
11178             {  0, +1 }
11179           };
11180
11181         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11182         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11183
11184         change->actual_trigger_ce_value = CustomValue[xx][yy];
11185         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11186       }
11187
11188       if (change->can_change && !change_done)
11189       {
11190         ChangeDelay[x][y] = 1;
11191         ChangeEvent[x][y] = trigger_event;
11192
11193         HandleElementChange(x, y, p);
11194
11195         change_done = TRUE;
11196       }
11197       else if (change->has_action)
11198       {
11199         ExecuteCustomElementAction(x, y, element, p);
11200         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11201       }
11202     }
11203   }
11204
11205   RECURSION_LOOP_DETECTION_END();
11206
11207   return change_done;
11208 }
11209
11210 static void PlayPlayerSound(struct PlayerInfo *player)
11211 {
11212   int jx = player->jx, jy = player->jy;
11213   int sound_element = player->artwork_element;
11214   int last_action = player->last_action_waiting;
11215   int action = player->action_waiting;
11216
11217   if (player->is_waiting)
11218   {
11219     if (action != last_action)
11220       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11221     else
11222       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11223   }
11224   else
11225   {
11226     if (action != last_action)
11227       StopSound(element_info[sound_element].sound[last_action]);
11228
11229     if (last_action == ACTION_SLEEPING)
11230       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11231   }
11232 }
11233
11234 static void PlayAllPlayersSound(void)
11235 {
11236   int i;
11237
11238   for (i = 0; i < MAX_PLAYERS; i++)
11239     if (stored_player[i].active)
11240       PlayPlayerSound(&stored_player[i]);
11241 }
11242
11243 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11244 {
11245   boolean last_waiting = player->is_waiting;
11246   int move_dir = player->MovDir;
11247
11248   player->dir_waiting = move_dir;
11249   player->last_action_waiting = player->action_waiting;
11250
11251   if (is_waiting)
11252   {
11253     if (!last_waiting)          // not waiting -> waiting
11254     {
11255       player->is_waiting = TRUE;
11256
11257       player->frame_counter_bored =
11258         FrameCounter +
11259         game.player_boring_delay_fixed +
11260         GetSimpleRandom(game.player_boring_delay_random);
11261       player->frame_counter_sleeping =
11262         FrameCounter +
11263         game.player_sleeping_delay_fixed +
11264         GetSimpleRandom(game.player_sleeping_delay_random);
11265
11266       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11267     }
11268
11269     if (game.player_sleeping_delay_fixed +
11270         game.player_sleeping_delay_random > 0 &&
11271         player->anim_delay_counter == 0 &&
11272         player->post_delay_counter == 0 &&
11273         FrameCounter >= player->frame_counter_sleeping)
11274       player->is_sleeping = TRUE;
11275     else if (game.player_boring_delay_fixed +
11276              game.player_boring_delay_random > 0 &&
11277              FrameCounter >= player->frame_counter_bored)
11278       player->is_bored = TRUE;
11279
11280     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11281                               player->is_bored ? ACTION_BORING :
11282                               ACTION_WAITING);
11283
11284     if (player->is_sleeping && player->use_murphy)
11285     {
11286       // special case for sleeping Murphy when leaning against non-free tile
11287
11288       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11289           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11290            !IS_MOVING(player->jx - 1, player->jy)))
11291         move_dir = MV_LEFT;
11292       else 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_RIGHT;
11296       else
11297         player->is_sleeping = FALSE;
11298
11299       player->dir_waiting = move_dir;
11300     }
11301
11302     if (player->is_sleeping)
11303     {
11304       if (player->num_special_action_sleeping > 0)
11305       {
11306         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11307         {
11308           int last_special_action = player->special_action_sleeping;
11309           int num_special_action = player->num_special_action_sleeping;
11310           int special_action =
11311             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11312              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11313              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11314              last_special_action + 1 : ACTION_SLEEPING);
11315           int special_graphic =
11316             el_act_dir2img(player->artwork_element, special_action, move_dir);
11317
11318           player->anim_delay_counter =
11319             graphic_info[special_graphic].anim_delay_fixed +
11320             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11321           player->post_delay_counter =
11322             graphic_info[special_graphic].post_delay_fixed +
11323             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11324
11325           player->special_action_sleeping = special_action;
11326         }
11327
11328         if (player->anim_delay_counter > 0)
11329         {
11330           player->action_waiting = player->special_action_sleeping;
11331           player->anim_delay_counter--;
11332         }
11333         else if (player->post_delay_counter > 0)
11334         {
11335           player->post_delay_counter--;
11336         }
11337       }
11338     }
11339     else if (player->is_bored)
11340     {
11341       if (player->num_special_action_bored > 0)
11342       {
11343         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11344         {
11345           int special_action =
11346             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11347           int special_graphic =
11348             el_act_dir2img(player->artwork_element, special_action, move_dir);
11349
11350           player->anim_delay_counter =
11351             graphic_info[special_graphic].anim_delay_fixed +
11352             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11353           player->post_delay_counter =
11354             graphic_info[special_graphic].post_delay_fixed +
11355             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11356
11357           player->special_action_bored = special_action;
11358         }
11359
11360         if (player->anim_delay_counter > 0)
11361         {
11362           player->action_waiting = player->special_action_bored;
11363           player->anim_delay_counter--;
11364         }
11365         else if (player->post_delay_counter > 0)
11366         {
11367           player->post_delay_counter--;
11368         }
11369       }
11370     }
11371   }
11372   else if (last_waiting)        // waiting -> not waiting
11373   {
11374     player->is_waiting = FALSE;
11375     player->is_bored = FALSE;
11376     player->is_sleeping = FALSE;
11377
11378     player->frame_counter_bored = -1;
11379     player->frame_counter_sleeping = -1;
11380
11381     player->anim_delay_counter = 0;
11382     player->post_delay_counter = 0;
11383
11384     player->dir_waiting = player->MovDir;
11385     player->action_waiting = ACTION_DEFAULT;
11386
11387     player->special_action_bored = ACTION_DEFAULT;
11388     player->special_action_sleeping = ACTION_DEFAULT;
11389   }
11390 }
11391
11392 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11393 {
11394   if ((!player->is_moving  && player->was_moving) ||
11395       (player->MovPos == 0 && player->was_moving) ||
11396       (player->is_snapping && !player->was_snapping) ||
11397       (player->is_dropping && !player->was_dropping))
11398   {
11399     if (!CheckSaveEngineSnapshotToList())
11400       return;
11401
11402     player->was_moving = FALSE;
11403     player->was_snapping = TRUE;
11404     player->was_dropping = TRUE;
11405   }
11406   else
11407   {
11408     if (player->is_moving)
11409       player->was_moving = TRUE;
11410
11411     if (!player->is_snapping)
11412       player->was_snapping = FALSE;
11413
11414     if (!player->is_dropping)
11415       player->was_dropping = FALSE;
11416   }
11417
11418   static struct MouseActionInfo mouse_action_last = { 0 };
11419   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11420   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11421
11422   if (new_released)
11423     CheckSaveEngineSnapshotToList();
11424
11425   mouse_action_last = mouse_action;
11426 }
11427
11428 static void CheckSingleStepMode(struct PlayerInfo *player)
11429 {
11430   if (tape.single_step && tape.recording && !tape.pausing)
11431   {
11432     // as it is called "single step mode", just return to pause mode when the
11433     // player stopped moving after one tile (or never starts moving at all)
11434     // (reverse logic needed here in case single step mode used in team mode)
11435     if (player->is_moving ||
11436         player->is_pushing ||
11437         player->is_dropping_pressed ||
11438         player->effective_mouse_action.button)
11439       game.enter_single_step_mode = FALSE;
11440   }
11441
11442   CheckSaveEngineSnapshot(player);
11443 }
11444
11445 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11446 {
11447   int left      = player_action & JOY_LEFT;
11448   int right     = player_action & JOY_RIGHT;
11449   int up        = player_action & JOY_UP;
11450   int down      = player_action & JOY_DOWN;
11451   int button1   = player_action & JOY_BUTTON_1;
11452   int button2   = player_action & JOY_BUTTON_2;
11453   int dx        = (left ? -1 : right ? 1 : 0);
11454   int dy        = (up   ? -1 : down  ? 1 : 0);
11455
11456   if (!player->active || tape.pausing)
11457     return 0;
11458
11459   if (player_action)
11460   {
11461     if (button1)
11462       SnapField(player, dx, dy);
11463     else
11464     {
11465       if (button2)
11466         DropElement(player);
11467
11468       MovePlayer(player, dx, dy);
11469     }
11470
11471     CheckSingleStepMode(player);
11472
11473     SetPlayerWaiting(player, FALSE);
11474
11475     return player_action;
11476   }
11477   else
11478   {
11479     // no actions for this player (no input at player's configured device)
11480
11481     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11482     SnapField(player, 0, 0);
11483     CheckGravityMovementWhenNotMoving(player);
11484
11485     if (player->MovPos == 0)
11486       SetPlayerWaiting(player, TRUE);
11487
11488     if (player->MovPos == 0)    // needed for tape.playing
11489       player->is_moving = FALSE;
11490
11491     player->is_dropping = FALSE;
11492     player->is_dropping_pressed = FALSE;
11493     player->drop_pressed_delay = 0;
11494
11495     CheckSingleStepMode(player);
11496
11497     return 0;
11498   }
11499 }
11500
11501 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11502                                          byte *tape_action)
11503 {
11504   if (!tape.use_mouse_actions)
11505     return;
11506
11507   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11508   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11509   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11510 }
11511
11512 static void SetTapeActionFromMouseAction(byte *tape_action,
11513                                          struct MouseActionInfo *mouse_action)
11514 {
11515   if (!tape.use_mouse_actions)
11516     return;
11517
11518   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11519   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11520   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11521 }
11522
11523 static void CheckLevelSolved(void)
11524 {
11525   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11526   {
11527     if (game_em.level_solved &&
11528         !game_em.game_over)                             // game won
11529     {
11530       LevelSolved();
11531
11532       game_em.game_over = TRUE;
11533
11534       game.all_players_gone = TRUE;
11535     }
11536
11537     if (game_em.game_over)                              // game lost
11538       game.all_players_gone = TRUE;
11539   }
11540   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11541   {
11542     if (game_sp.level_solved &&
11543         !game_sp.game_over)                             // game won
11544     {
11545       LevelSolved();
11546
11547       game_sp.game_over = TRUE;
11548
11549       game.all_players_gone = TRUE;
11550     }
11551
11552     if (game_sp.game_over)                              // game lost
11553       game.all_players_gone = TRUE;
11554   }
11555   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11556   {
11557     if (game_mm.level_solved &&
11558         !game_mm.game_over)                             // game won
11559     {
11560       LevelSolved();
11561
11562       game_mm.game_over = TRUE;
11563
11564       game.all_players_gone = TRUE;
11565     }
11566
11567     if (game_mm.game_over)                              // game lost
11568       game.all_players_gone = TRUE;
11569   }
11570 }
11571
11572 static void CheckLevelTime_StepCounter(void)
11573 {
11574   int i;
11575
11576   TimePlayed++;
11577
11578   if (TimeLeft > 0)
11579   {
11580     TimeLeft--;
11581
11582     if (TimeLeft <= 10 && game.time_limit && !game.LevelSolved)
11583       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11584
11585     game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11586
11587     DisplayGameControlValues();
11588
11589     if (!TimeLeft && game.time_limit && !game.LevelSolved)
11590       for (i = 0; i < MAX_PLAYERS; i++)
11591         KillPlayer(&stored_player[i]);
11592   }
11593   else if (game.no_level_time_limit && !game.all_players_gone)
11594   {
11595     game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11596
11597     DisplayGameControlValues();
11598   }
11599 }
11600
11601 static void CheckLevelTime(void)
11602 {
11603   int i;
11604
11605   if (TimeFrames >= FRAMES_PER_SECOND)
11606   {
11607     TimeFrames = 0;
11608     TapeTime++;
11609
11610     for (i = 0; i < MAX_PLAYERS; i++)
11611     {
11612       struct PlayerInfo *player = &stored_player[i];
11613
11614       if (SHIELD_ON(player))
11615       {
11616         player->shield_normal_time_left--;
11617
11618         if (player->shield_deadly_time_left > 0)
11619           player->shield_deadly_time_left--;
11620       }
11621     }
11622
11623     if (!game.LevelSolved && !level.use_step_counter)
11624     {
11625       TimePlayed++;
11626
11627       if (TimeLeft > 0)
11628       {
11629         TimeLeft--;
11630
11631         if (TimeLeft <= 10 && game.time_limit)
11632           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11633
11634         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11635            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11636
11637         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11638
11639         if (!TimeLeft && game.time_limit)
11640         {
11641           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11642             game_em.lev->killed_out_of_time = TRUE;
11643           else
11644             for (i = 0; i < MAX_PLAYERS; i++)
11645               KillPlayer(&stored_player[i]);
11646         }
11647       }
11648       else if (game.no_level_time_limit && !game.all_players_gone)
11649       {
11650         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11651       }
11652
11653       game_em.lev->time = (game.no_level_time_limit ? TimePlayed : TimeLeft);
11654     }
11655
11656     if (tape.recording || tape.playing)
11657       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11658   }
11659
11660   if (tape.recording || tape.playing)
11661     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11662
11663   UpdateAndDisplayGameControlValues();
11664 }
11665
11666 void AdvanceFrameAndPlayerCounters(int player_nr)
11667 {
11668   int i;
11669
11670   // advance frame counters (global frame counter and time frame counter)
11671   FrameCounter++;
11672   TimeFrames++;
11673
11674   // advance player counters (counters for move delay, move animation etc.)
11675   for (i = 0; i < MAX_PLAYERS; i++)
11676   {
11677     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11678     int move_delay_value = stored_player[i].move_delay_value;
11679     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11680
11681     if (!advance_player_counters)       // not all players may be affected
11682       continue;
11683
11684     if (move_frames == 0)       // less than one move per game frame
11685     {
11686       int stepsize = TILEX / move_delay_value;
11687       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11688       int count = (stored_player[i].is_moving ?
11689                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11690
11691       if (count % delay == 0)
11692         move_frames = 1;
11693     }
11694
11695     stored_player[i].Frame += move_frames;
11696
11697     if (stored_player[i].MovPos != 0)
11698       stored_player[i].StepFrame += move_frames;
11699
11700     if (stored_player[i].move_delay > 0)
11701       stored_player[i].move_delay--;
11702
11703     // due to bugs in previous versions, counter must count up, not down
11704     if (stored_player[i].push_delay != -1)
11705       stored_player[i].push_delay++;
11706
11707     if (stored_player[i].drop_delay > 0)
11708       stored_player[i].drop_delay--;
11709
11710     if (stored_player[i].is_dropping_pressed)
11711       stored_player[i].drop_pressed_delay++;
11712   }
11713 }
11714
11715 void AdvanceFrameCounter(void)
11716 {
11717   FrameCounter++;
11718 }
11719
11720 void AdvanceGfxFrame(void)
11721 {
11722   int x, y;
11723
11724   SCAN_PLAYFIELD(x, y)
11725   {
11726     GfxFrame[x][y]++;
11727   }
11728 }
11729
11730 static void HandleMouseAction(struct MouseActionInfo *mouse_action,
11731                               struct MouseActionInfo *mouse_action_last)
11732 {
11733   if (mouse_action->button)
11734   {
11735     int new_button = (mouse_action->button && mouse_action_last->button == 0);
11736     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action->button);
11737     int x = mouse_action->lx;
11738     int y = mouse_action->ly;
11739     int element = Tile[x][y];
11740
11741     if (new_button)
11742     {
11743       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
11744       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
11745                                          ch_button);
11746     }
11747
11748     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
11749     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
11750                                        ch_button);
11751
11752     if (level.use_step_counter)
11753     {
11754       boolean counted_click = FALSE;
11755
11756       // element clicked that can change when clicked/pressed
11757       if (CAN_CHANGE_OR_HAS_ACTION(element) &&
11758           (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
11759            HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE)))
11760         counted_click = TRUE;
11761
11762       // element clicked that can trigger change when clicked/pressed
11763       if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] ||
11764           trigger_events[element][CE_MOUSE_PRESSED_ON_X])
11765         counted_click = TRUE;
11766
11767       if (new_button && counted_click)
11768         CheckLevelTime_StepCounter();
11769     }
11770   }
11771 }
11772
11773 void StartGameActions(boolean init_network_game, boolean record_tape,
11774                       int random_seed)
11775 {
11776   unsigned int new_random_seed = InitRND(random_seed);
11777
11778   if (record_tape)
11779     TapeStartRecording(new_random_seed);
11780
11781   if (setup.auto_pause_on_start && !tape.pausing)
11782     TapeTogglePause(TAPE_TOGGLE_MANUAL);
11783
11784   if (init_network_game)
11785   {
11786     SendToServer_LevelFile();
11787     SendToServer_StartPlaying();
11788
11789     return;
11790   }
11791
11792   InitGame();
11793 }
11794
11795 static void GameActionsExt(void)
11796 {
11797 #if 0
11798   static unsigned int game_frame_delay = 0;
11799 #endif
11800   unsigned int game_frame_delay_value;
11801   byte *recorded_player_action;
11802   byte summarized_player_action = 0;
11803   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11804   int i;
11805
11806   // detect endless loops, caused by custom element programming
11807   if (recursion_loop_detected && recursion_loop_depth == 0)
11808   {
11809     char *message = getStringCat3("Internal Error! Element ",
11810                                   EL_NAME(recursion_loop_element),
11811                                   " caused endless loop! Quit the game?");
11812
11813     Warn("element '%s' caused endless loop in game engine",
11814          EL_NAME(recursion_loop_element));
11815
11816     RequestQuitGameExt(program.headless, level_editor_test_game, message);
11817
11818     recursion_loop_detected = FALSE;    // if game should be continued
11819
11820     free(message);
11821
11822     return;
11823   }
11824
11825   if (game.restart_level)
11826     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11827
11828   CheckLevelSolved();
11829
11830   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11831     GameWon();
11832
11833   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11834     TapeStop();
11835
11836   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11837     return;
11838
11839   game_frame_delay_value =
11840     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11841
11842   if (tape.playing && tape.warp_forward && !tape.pausing)
11843     game_frame_delay_value = 0;
11844
11845   SetVideoFrameDelay(game_frame_delay_value);
11846
11847   // (de)activate virtual buttons depending on current game status
11848   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11849   {
11850     if (game.all_players_gone)  // if no players there to be controlled anymore
11851       SetOverlayActive(FALSE);
11852     else if (!tape.playing)     // if game continues after tape stopped playing
11853       SetOverlayActive(TRUE);
11854   }
11855
11856 #if 0
11857 #if 0
11858   // ---------- main game synchronization point ----------
11859
11860   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11861
11862   Debug("game:playing:skip", "skip == %d", skip);
11863
11864 #else
11865   // ---------- main game synchronization point ----------
11866
11867   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11868 #endif
11869 #endif
11870
11871   if (network_playing && !network_player_action_received)
11872   {
11873     // try to get network player actions in time
11874
11875     // last chance to get network player actions without main loop delay
11876     HandleNetworking();
11877
11878     // game was quit by network peer
11879     if (game_status != GAME_MODE_PLAYING)
11880       return;
11881
11882     // check if network player actions still missing and game still running
11883     if (!network_player_action_received && !checkGameEnded())
11884       return;           // failed to get network player actions in time
11885
11886     // do not yet reset "network_player_action_received" (for tape.pausing)
11887   }
11888
11889   if (tape.pausing)
11890     return;
11891
11892   // at this point we know that we really continue executing the game
11893
11894   network_player_action_received = FALSE;
11895
11896   // when playing tape, read previously recorded player input from tape data
11897   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11898
11899   local_player->effective_mouse_action = local_player->mouse_action;
11900
11901   if (recorded_player_action != NULL)
11902     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11903                                  recorded_player_action);
11904
11905   // TapePlayAction() may return NULL when toggling to "pause before death"
11906   if (tape.pausing)
11907     return;
11908
11909   if (tape.set_centered_player)
11910   {
11911     game.centered_player_nr_next = tape.centered_player_nr_next;
11912     game.set_centered_player = TRUE;
11913   }
11914
11915   for (i = 0; i < MAX_PLAYERS; i++)
11916   {
11917     summarized_player_action |= stored_player[i].action;
11918
11919     if (!network_playing && (game.team_mode || tape.playing))
11920       stored_player[i].effective_action = stored_player[i].action;
11921   }
11922
11923   if (network_playing && !checkGameEnded())
11924     SendToServer_MovePlayer(summarized_player_action);
11925
11926   // summarize all actions at local players mapped input device position
11927   // (this allows using different input devices in single player mode)
11928   if (!network.enabled && !game.team_mode)
11929     stored_player[map_player_action[local_player->index_nr]].effective_action =
11930       summarized_player_action;
11931
11932   // summarize all actions at centered player in local team mode
11933   if (tape.recording &&
11934       setup.team_mode && !network.enabled &&
11935       setup.input_on_focus &&
11936       game.centered_player_nr != -1)
11937   {
11938     for (i = 0; i < MAX_PLAYERS; i++)
11939       stored_player[map_player_action[i]].effective_action =
11940         (i == game.centered_player_nr ? summarized_player_action : 0);
11941   }
11942
11943   if (recorded_player_action != NULL)
11944     for (i = 0; i < MAX_PLAYERS; i++)
11945       stored_player[i].effective_action = recorded_player_action[i];
11946
11947   for (i = 0; i < MAX_PLAYERS; i++)
11948   {
11949     tape_action[i] = stored_player[i].effective_action;
11950
11951     /* (this may happen in the RND game engine if a player was not present on
11952        the playfield on level start, but appeared later from a custom element */
11953     if (setup.team_mode &&
11954         tape.recording &&
11955         tape_action[i] &&
11956         !tape.player_participates[i])
11957       tape.player_participates[i] = TRUE;
11958   }
11959
11960   SetTapeActionFromMouseAction(tape_action,
11961                                &local_player->effective_mouse_action);
11962
11963   // only record actions from input devices, but not programmed actions
11964   if (tape.recording)
11965     TapeRecordAction(tape_action);
11966
11967   // remember if game was played (especially after tape stopped playing)
11968   if (!tape.playing && summarized_player_action && !checkGameFailed())
11969     game.GamePlayed = TRUE;
11970
11971 #if USE_NEW_PLAYER_ASSIGNMENTS
11972   // !!! also map player actions in single player mode !!!
11973   // if (game.team_mode)
11974   if (1)
11975   {
11976     byte mapped_action[MAX_PLAYERS];
11977
11978 #if DEBUG_PLAYER_ACTIONS
11979     for (i = 0; i < MAX_PLAYERS; i++)
11980       DebugContinued("", "%d, ", stored_player[i].effective_action);
11981 #endif
11982
11983     for (i = 0; i < MAX_PLAYERS; i++)
11984       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11985
11986     for (i = 0; i < MAX_PLAYERS; i++)
11987       stored_player[i].effective_action = mapped_action[i];
11988
11989 #if DEBUG_PLAYER_ACTIONS
11990     DebugContinued("", "=> ");
11991     for (i = 0; i < MAX_PLAYERS; i++)
11992       DebugContinued("", "%d, ", stored_player[i].effective_action);
11993     DebugContinued("game:playing:player", "\n");
11994 #endif
11995   }
11996 #if DEBUG_PLAYER_ACTIONS
11997   else
11998   {
11999     for (i = 0; i < MAX_PLAYERS; i++)
12000       DebugContinued("", "%d, ", stored_player[i].effective_action);
12001     DebugContinued("game:playing:player", "\n");
12002   }
12003 #endif
12004 #endif
12005
12006   for (i = 0; i < MAX_PLAYERS; i++)
12007   {
12008     // allow engine snapshot in case of changed movement attempt
12009     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
12010         (stored_player[i].effective_action & KEY_MOTION))
12011       game.snapshot.changed_action = TRUE;
12012
12013     // allow engine snapshot in case of snapping/dropping attempt
12014     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
12015         (stored_player[i].effective_action & KEY_BUTTON) != 0)
12016       game.snapshot.changed_action = TRUE;
12017
12018     game.snapshot.last_action[i] = stored_player[i].effective_action;
12019   }
12020
12021   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12022   {
12023     GameActions_EM_Main();
12024   }
12025   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12026   {
12027     GameActions_SP_Main();
12028   }
12029   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
12030   {
12031     GameActions_MM_Main();
12032   }
12033   else
12034   {
12035     GameActions_RND_Main();
12036   }
12037
12038   BlitScreenToBitmap(backbuffer);
12039
12040   CheckLevelSolved();
12041   CheckLevelTime();
12042
12043   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
12044
12045   if (global.show_frames_per_second)
12046   {
12047     static unsigned int fps_counter = 0;
12048     static int fps_frames = 0;
12049     unsigned int fps_delay_ms = Counter() - fps_counter;
12050
12051     fps_frames++;
12052
12053     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
12054     {
12055       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12056
12057       fps_frames = 0;
12058       fps_counter = Counter();
12059
12060       // always draw FPS to screen after FPS value was updated
12061       redraw_mask |= REDRAW_FPS;
12062     }
12063
12064     // only draw FPS if no screen areas are deactivated (invisible warp mode)
12065     if (GetDrawDeactivationMask() == REDRAW_NONE)
12066       redraw_mask |= REDRAW_FPS;
12067   }
12068 }
12069
12070 static void GameActions_CheckSaveEngineSnapshot(void)
12071 {
12072   if (!game.snapshot.save_snapshot)
12073     return;
12074
12075   // clear flag for saving snapshot _before_ saving snapshot
12076   game.snapshot.save_snapshot = FALSE;
12077
12078   SaveEngineSnapshotToList();
12079 }
12080
12081 void GameActions(void)
12082 {
12083   GameActionsExt();
12084
12085   GameActions_CheckSaveEngineSnapshot();
12086 }
12087
12088 void GameActions_EM_Main(void)
12089 {
12090   byte effective_action[MAX_PLAYERS];
12091   int i;
12092
12093   for (i = 0; i < MAX_PLAYERS; i++)
12094     effective_action[i] = stored_player[i].effective_action;
12095
12096   GameActions_EM(effective_action);
12097 }
12098
12099 void GameActions_SP_Main(void)
12100 {
12101   byte effective_action[MAX_PLAYERS];
12102   int i;
12103
12104   for (i = 0; i < MAX_PLAYERS; i++)
12105     effective_action[i] = stored_player[i].effective_action;
12106
12107   GameActions_SP(effective_action);
12108
12109   for (i = 0; i < MAX_PLAYERS; i++)
12110   {
12111     if (stored_player[i].force_dropping)
12112       stored_player[i].action |= KEY_BUTTON_DROP;
12113
12114     stored_player[i].force_dropping = FALSE;
12115   }
12116 }
12117
12118 void GameActions_MM_Main(void)
12119 {
12120   AdvanceGfxFrame();
12121
12122   GameActions_MM(local_player->effective_mouse_action);
12123 }
12124
12125 void GameActions_RND_Main(void)
12126 {
12127   GameActions_RND();
12128 }
12129
12130 void GameActions_RND(void)
12131 {
12132   static struct MouseActionInfo mouse_action_last = { 0 };
12133   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12134   int magic_wall_x = 0, magic_wall_y = 0;
12135   int i, x, y, element, graphic, last_gfx_frame;
12136
12137   InitPlayfieldScanModeVars();
12138
12139   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12140   {
12141     SCAN_PLAYFIELD(x, y)
12142     {
12143       ChangeCount[x][y] = 0;
12144       ChangeEvent[x][y] = -1;
12145     }
12146   }
12147
12148   if (game.set_centered_player)
12149   {
12150     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12151
12152     // switching to "all players" only possible if all players fit to screen
12153     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12154     {
12155       game.centered_player_nr_next = game.centered_player_nr;
12156       game.set_centered_player = FALSE;
12157     }
12158
12159     // do not switch focus to non-existing (or non-active) player
12160     if (game.centered_player_nr_next >= 0 &&
12161         !stored_player[game.centered_player_nr_next].active)
12162     {
12163       game.centered_player_nr_next = game.centered_player_nr;
12164       game.set_centered_player = FALSE;
12165     }
12166   }
12167
12168   if (game.set_centered_player &&
12169       ScreenMovPos == 0)        // screen currently aligned at tile position
12170   {
12171     int sx, sy;
12172
12173     if (game.centered_player_nr_next == -1)
12174     {
12175       setScreenCenteredToAllPlayers(&sx, &sy);
12176     }
12177     else
12178     {
12179       sx = stored_player[game.centered_player_nr_next].jx;
12180       sy = stored_player[game.centered_player_nr_next].jy;
12181     }
12182
12183     game.centered_player_nr = game.centered_player_nr_next;
12184     game.set_centered_player = FALSE;
12185
12186     DrawRelocateScreen(0, 0, sx, sy, TRUE, setup.quick_switch);
12187     DrawGameDoorValues();
12188   }
12189
12190   // check single step mode (set flag and clear again if any player is active)
12191   game.enter_single_step_mode =
12192     (tape.single_step && tape.recording && !tape.pausing);
12193
12194   for (i = 0; i < MAX_PLAYERS; i++)
12195   {
12196     int actual_player_action = stored_player[i].effective_action;
12197
12198 #if 1
12199     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12200        - rnd_equinox_tetrachloride 048
12201        - rnd_equinox_tetrachloride_ii 096
12202        - rnd_emanuel_schmieg 002
12203        - doctor_sloan_ww 001, 020
12204     */
12205     if (stored_player[i].MovPos == 0)
12206       CheckGravityMovement(&stored_player[i]);
12207 #endif
12208
12209     // overwrite programmed action with tape action
12210     if (stored_player[i].programmed_action)
12211       actual_player_action = stored_player[i].programmed_action;
12212
12213     PlayerActions(&stored_player[i], actual_player_action);
12214
12215     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12216   }
12217
12218   // single step pause mode may already have been toggled by "ScrollPlayer()"
12219   if (game.enter_single_step_mode && !tape.pausing)
12220     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12221
12222   ScrollScreen(NULL, SCROLL_GO_ON);
12223
12224   /* for backwards compatibility, the following code emulates a fixed bug that
12225      occured when pushing elements (causing elements that just made their last
12226      pushing step to already (if possible) make their first falling step in the
12227      same game frame, which is bad); this code is also needed to use the famous
12228      "spring push bug" which is used in older levels and might be wanted to be
12229      used also in newer levels, but in this case the buggy pushing code is only
12230      affecting the "spring" element and no other elements */
12231
12232   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12233   {
12234     for (i = 0; i < MAX_PLAYERS; i++)
12235     {
12236       struct PlayerInfo *player = &stored_player[i];
12237       int x = player->jx;
12238       int y = player->jy;
12239
12240       if (player->active && player->is_pushing && player->is_moving &&
12241           IS_MOVING(x, y) &&
12242           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12243            Tile[x][y] == EL_SPRING))
12244       {
12245         ContinueMoving(x, y);
12246
12247         // continue moving after pushing (this is actually a bug)
12248         if (!IS_MOVING(x, y))
12249           Stop[x][y] = FALSE;
12250       }
12251     }
12252   }
12253
12254   SCAN_PLAYFIELD(x, y)
12255   {
12256     Last[x][y] = Tile[x][y];
12257
12258     ChangeCount[x][y] = 0;
12259     ChangeEvent[x][y] = -1;
12260
12261     // this must be handled before main playfield loop
12262     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12263     {
12264       MovDelay[x][y]--;
12265       if (MovDelay[x][y] <= 0)
12266         RemoveField(x, y);
12267     }
12268
12269     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12270     {
12271       MovDelay[x][y]--;
12272       if (MovDelay[x][y] <= 0)
12273       {
12274         int element = Store[x][y];
12275         int move_direction = MovDir[x][y];
12276         int player_index_bit = Store2[x][y];
12277
12278         Store[x][y] = 0;
12279         Store2[x][y] = 0;
12280
12281         RemoveField(x, y);
12282         TEST_DrawLevelField(x, y);
12283
12284         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12285
12286         if (IS_ENVELOPE(element))
12287           local_player->show_envelope = element;
12288       }
12289     }
12290
12291 #if DEBUG
12292     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12293     {
12294       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12295             x, y);
12296       Debug("game:playing:GameActions_RND", "This should never happen!");
12297
12298       ChangePage[x][y] = -1;
12299     }
12300 #endif
12301
12302     Stop[x][y] = FALSE;
12303     if (WasJustMoving[x][y] > 0)
12304       WasJustMoving[x][y]--;
12305     if (WasJustFalling[x][y] > 0)
12306       WasJustFalling[x][y]--;
12307     if (CheckCollision[x][y] > 0)
12308       CheckCollision[x][y]--;
12309     if (CheckImpact[x][y] > 0)
12310       CheckImpact[x][y]--;
12311
12312     GfxFrame[x][y]++;
12313
12314     /* reset finished pushing action (not done in ContinueMoving() to allow
12315        continuous pushing animation for elements with zero push delay) */
12316     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12317     {
12318       ResetGfxAnimation(x, y);
12319       TEST_DrawLevelField(x, y);
12320     }
12321
12322 #if DEBUG
12323     if (IS_BLOCKED(x, y))
12324     {
12325       int oldx, oldy;
12326
12327       Blocked2Moving(x, y, &oldx, &oldy);
12328       if (!IS_MOVING(oldx, oldy))
12329       {
12330         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12331         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12332         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12333         Debug("game:playing:GameActions_RND", "This should never happen!");
12334       }
12335     }
12336 #endif
12337   }
12338
12339   HandleMouseAction(&mouse_action, &mouse_action_last);
12340
12341   SCAN_PLAYFIELD(x, y)
12342   {
12343     element = Tile[x][y];
12344     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12345     last_gfx_frame = GfxFrame[x][y];
12346
12347     if (element == EL_EMPTY)
12348       graphic = el2img(GfxElementEmpty[x][y]);
12349
12350     ResetGfxFrame(x, y);
12351
12352     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12353       DrawLevelGraphicAnimation(x, y, graphic);
12354
12355     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12356         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12357       ResetRandomAnimationValue(x, y);
12358
12359     SetRandomAnimationValue(x, y);
12360
12361     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12362
12363     if (IS_INACTIVE(element))
12364     {
12365       if (IS_ANIMATED(graphic))
12366         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12367
12368       continue;
12369     }
12370
12371     // this may take place after moving, so 'element' may have changed
12372     if (IS_CHANGING(x, y) &&
12373         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12374     {
12375       int page = element_info[element].event_page_nr[CE_DELAY];
12376
12377       HandleElementChange(x, y, page);
12378
12379       element = Tile[x][y];
12380       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12381     }
12382
12383     CheckNextToConditions(x, y);
12384
12385     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12386     {
12387       StartMoving(x, y);
12388
12389       element = Tile[x][y];
12390       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12391
12392       if (IS_ANIMATED(graphic) &&
12393           !IS_MOVING(x, y) &&
12394           !Stop[x][y])
12395         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12396
12397       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12398         TEST_DrawTwinkleOnField(x, y);
12399     }
12400     else if (element == EL_ACID)
12401     {
12402       if (!Stop[x][y])
12403         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12404     }
12405     else if ((element == EL_EXIT_OPEN ||
12406               element == EL_EM_EXIT_OPEN ||
12407               element == EL_SP_EXIT_OPEN ||
12408               element == EL_STEEL_EXIT_OPEN ||
12409               element == EL_EM_STEEL_EXIT_OPEN ||
12410               element == EL_SP_TERMINAL ||
12411               element == EL_SP_TERMINAL_ACTIVE ||
12412               element == EL_EXTRA_TIME ||
12413               element == EL_SHIELD_NORMAL ||
12414               element == EL_SHIELD_DEADLY) &&
12415              IS_ANIMATED(graphic))
12416       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12417     else if (IS_MOVING(x, y))
12418       ContinueMoving(x, y);
12419     else if (IS_ACTIVE_BOMB(element))
12420       CheckDynamite(x, y);
12421     else if (element == EL_AMOEBA_GROWING)
12422       AmoebaGrowing(x, y);
12423     else if (element == EL_AMOEBA_SHRINKING)
12424       AmoebaShrinking(x, y);
12425
12426 #if !USE_NEW_AMOEBA_CODE
12427     else if (IS_AMOEBALIVE(element))
12428       AmoebaReproduce(x, y);
12429 #endif
12430
12431     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12432       Life(x, y);
12433     else if (element == EL_EXIT_CLOSED)
12434       CheckExit(x, y);
12435     else if (element == EL_EM_EXIT_CLOSED)
12436       CheckExitEM(x, y);
12437     else if (element == EL_STEEL_EXIT_CLOSED)
12438       CheckExitSteel(x, y);
12439     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12440       CheckExitSteelEM(x, y);
12441     else if (element == EL_SP_EXIT_CLOSED)
12442       CheckExitSP(x, y);
12443     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12444              element == EL_EXPANDABLE_STEELWALL_GROWING)
12445       WallGrowing(x, y);
12446     else if (element == EL_EXPANDABLE_WALL ||
12447              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12448              element == EL_EXPANDABLE_WALL_VERTICAL ||
12449              element == EL_EXPANDABLE_WALL_ANY ||
12450              element == EL_BD_EXPANDABLE_WALL ||
12451              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12452              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12453              element == EL_EXPANDABLE_STEELWALL_ANY)
12454       CheckWallGrowing(x, y);
12455     else if (element == EL_FLAMES)
12456       CheckForDragon(x, y);
12457     else if (element == EL_EXPLOSION)
12458       ; // drawing of correct explosion animation is handled separately
12459     else if (element == EL_ELEMENT_SNAPPING ||
12460              element == EL_DIAGONAL_SHRINKING ||
12461              element == EL_DIAGONAL_GROWING)
12462     {
12463       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y], GfxDir[x][y]);
12464
12465       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12466     }
12467     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12468       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12469
12470     if (IS_BELT_ACTIVE(element))
12471       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12472
12473     if (game.magic_wall_active)
12474     {
12475       int jx = local_player->jx, jy = local_player->jy;
12476
12477       // play the element sound at the position nearest to the player
12478       if ((element == EL_MAGIC_WALL_FULL ||
12479            element == EL_MAGIC_WALL_ACTIVE ||
12480            element == EL_MAGIC_WALL_EMPTYING ||
12481            element == EL_BD_MAGIC_WALL_FULL ||
12482            element == EL_BD_MAGIC_WALL_ACTIVE ||
12483            element == EL_BD_MAGIC_WALL_EMPTYING ||
12484            element == EL_DC_MAGIC_WALL_FULL ||
12485            element == EL_DC_MAGIC_WALL_ACTIVE ||
12486            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12487           ABS(x - jx) + ABS(y - jy) <
12488           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12489       {
12490         magic_wall_x = x;
12491         magic_wall_y = y;
12492       }
12493     }
12494   }
12495
12496 #if USE_NEW_AMOEBA_CODE
12497   // new experimental amoeba growth stuff
12498   if (!(FrameCounter % 8))
12499   {
12500     static unsigned int random = 1684108901;
12501
12502     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12503     {
12504       x = RND(lev_fieldx);
12505       y = RND(lev_fieldy);
12506       element = Tile[x][y];
12507
12508       if (!IS_PLAYER(x, y) &&
12509           (element == EL_EMPTY ||
12510            CAN_GROW_INTO(element) ||
12511            element == EL_QUICKSAND_EMPTY ||
12512            element == EL_QUICKSAND_FAST_EMPTY ||
12513            element == EL_ACID_SPLASH_LEFT ||
12514            element == EL_ACID_SPLASH_RIGHT))
12515       {
12516         if ((IN_LEV_FIELD(x, y - 1) && Tile[x][y - 1] == EL_AMOEBA_WET) ||
12517             (IN_LEV_FIELD(x - 1, y) && Tile[x - 1][y] == EL_AMOEBA_WET) ||
12518             (IN_LEV_FIELD(x + 1, y) && Tile[x + 1][y] == EL_AMOEBA_WET) ||
12519             (IN_LEV_FIELD(x, y + 1) && Tile[x][y + 1] == EL_AMOEBA_WET))
12520           Tile[x][y] = EL_AMOEBA_DROP;
12521       }
12522
12523       random = random * 129 + 1;
12524     }
12525   }
12526 #endif
12527
12528   game.explosions_delayed = FALSE;
12529
12530   SCAN_PLAYFIELD(x, y)
12531   {
12532     element = Tile[x][y];
12533
12534     if (ExplodeField[x][y])
12535       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12536     else if (element == EL_EXPLOSION)
12537       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12538
12539     ExplodeField[x][y] = EX_TYPE_NONE;
12540   }
12541
12542   game.explosions_delayed = TRUE;
12543
12544   if (game.magic_wall_active)
12545   {
12546     if (!(game.magic_wall_time_left % 4))
12547     {
12548       int element = Tile[magic_wall_x][magic_wall_y];
12549
12550       if (element == EL_BD_MAGIC_WALL_FULL ||
12551           element == EL_BD_MAGIC_WALL_ACTIVE ||
12552           element == EL_BD_MAGIC_WALL_EMPTYING)
12553         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12554       else if (element == EL_DC_MAGIC_WALL_FULL ||
12555                element == EL_DC_MAGIC_WALL_ACTIVE ||
12556                element == EL_DC_MAGIC_WALL_EMPTYING)
12557         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12558       else
12559         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12560     }
12561
12562     if (game.magic_wall_time_left > 0)
12563     {
12564       game.magic_wall_time_left--;
12565
12566       if (!game.magic_wall_time_left)
12567       {
12568         SCAN_PLAYFIELD(x, y)
12569         {
12570           element = Tile[x][y];
12571
12572           if (element == EL_MAGIC_WALL_ACTIVE ||
12573               element == EL_MAGIC_WALL_FULL)
12574           {
12575             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12576             TEST_DrawLevelField(x, y);
12577           }
12578           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12579                    element == EL_BD_MAGIC_WALL_FULL)
12580           {
12581             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12582             TEST_DrawLevelField(x, y);
12583           }
12584           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12585                    element == EL_DC_MAGIC_WALL_FULL)
12586           {
12587             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12588             TEST_DrawLevelField(x, y);
12589           }
12590         }
12591
12592         game.magic_wall_active = FALSE;
12593       }
12594     }
12595   }
12596
12597   if (game.light_time_left > 0)
12598   {
12599     game.light_time_left--;
12600
12601     if (game.light_time_left == 0)
12602       RedrawAllLightSwitchesAndInvisibleElements();
12603   }
12604
12605   if (game.timegate_time_left > 0)
12606   {
12607     game.timegate_time_left--;
12608
12609     if (game.timegate_time_left == 0)
12610       CloseAllOpenTimegates();
12611   }
12612
12613   if (game.lenses_time_left > 0)
12614   {
12615     game.lenses_time_left--;
12616
12617     if (game.lenses_time_left == 0)
12618       RedrawAllInvisibleElementsForLenses();
12619   }
12620
12621   if (game.magnify_time_left > 0)
12622   {
12623     game.magnify_time_left--;
12624
12625     if (game.magnify_time_left == 0)
12626       RedrawAllInvisibleElementsForMagnifier();
12627   }
12628
12629   for (i = 0; i < MAX_PLAYERS; i++)
12630   {
12631     struct PlayerInfo *player = &stored_player[i];
12632
12633     if (SHIELD_ON(player))
12634     {
12635       if (player->shield_deadly_time_left)
12636         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12637       else if (player->shield_normal_time_left)
12638         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12639     }
12640   }
12641
12642 #if USE_DELAYED_GFX_REDRAW
12643   SCAN_PLAYFIELD(x, y)
12644   {
12645     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12646     {
12647       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12648          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12649
12650       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12651         DrawLevelField(x, y);
12652
12653       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12654         DrawLevelFieldCrumbled(x, y);
12655
12656       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12657         DrawLevelFieldCrumbledNeighbours(x, y);
12658
12659       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12660         DrawTwinkleOnField(x, y);
12661     }
12662
12663     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12664   }
12665 #endif
12666
12667   DrawAllPlayers();
12668   PlayAllPlayersSound();
12669
12670   for (i = 0; i < MAX_PLAYERS; i++)
12671   {
12672     struct PlayerInfo *player = &stored_player[i];
12673
12674     if (player->show_envelope != 0 && (!player->active ||
12675                                        player->MovPos == 0))
12676     {
12677       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12678
12679       player->show_envelope = 0;
12680     }
12681   }
12682
12683   // use random number generator in every frame to make it less predictable
12684   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12685     RND(1);
12686
12687   mouse_action_last = mouse_action;
12688 }
12689
12690 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12691 {
12692   int min_x = x, min_y = y, max_x = x, max_y = y;
12693   int scr_fieldx = getScreenFieldSizeX();
12694   int scr_fieldy = getScreenFieldSizeY();
12695   int i;
12696
12697   for (i = 0; i < MAX_PLAYERS; i++)
12698   {
12699     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12700
12701     if (!stored_player[i].active || &stored_player[i] == player)
12702       continue;
12703
12704     min_x = MIN(min_x, jx);
12705     min_y = MIN(min_y, jy);
12706     max_x = MAX(max_x, jx);
12707     max_y = MAX(max_y, jy);
12708   }
12709
12710   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12711 }
12712
12713 static boolean AllPlayersInVisibleScreen(void)
12714 {
12715   int i;
12716
12717   for (i = 0; i < MAX_PLAYERS; i++)
12718   {
12719     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12720
12721     if (!stored_player[i].active)
12722       continue;
12723
12724     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12725       return FALSE;
12726   }
12727
12728   return TRUE;
12729 }
12730
12731 void ScrollLevel(int dx, int dy)
12732 {
12733   int scroll_offset = 2 * TILEX_VAR;
12734   int x, y;
12735
12736   BlitBitmap(drawto_field, drawto_field,
12737              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12738              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12739              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12740              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12741              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12742              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12743
12744   if (dx != 0)
12745   {
12746     x = (dx == 1 ? BX1 : BX2);
12747     for (y = BY1; y <= BY2; y++)
12748       DrawScreenField(x, y);
12749   }
12750
12751   if (dy != 0)
12752   {
12753     y = (dy == 1 ? BY1 : BY2);
12754     for (x = BX1; x <= BX2; x++)
12755       DrawScreenField(x, y);
12756   }
12757
12758   redraw_mask |= REDRAW_FIELD;
12759 }
12760
12761 static boolean canFallDown(struct PlayerInfo *player)
12762 {
12763   int jx = player->jx, jy = player->jy;
12764
12765   return (IN_LEV_FIELD(jx, jy + 1) &&
12766           (IS_FREE(jx, jy + 1) ||
12767            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12768           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12769           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12770 }
12771
12772 static boolean canPassField(int x, int y, int move_dir)
12773 {
12774   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12775   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12776   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12777   int nextx = x + dx;
12778   int nexty = y + dy;
12779   int element = Tile[x][y];
12780
12781   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12782           !CAN_MOVE(element) &&
12783           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12784           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12785           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12786 }
12787
12788 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12789 {
12790   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12791   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12792   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12793   int newx = x + dx;
12794   int newy = y + dy;
12795
12796   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12797           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12798           (IS_DIGGABLE(Tile[newx][newy]) ||
12799            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12800            canPassField(newx, newy, move_dir)));
12801 }
12802
12803 static void CheckGravityMovement(struct PlayerInfo *player)
12804 {
12805   if (player->gravity && !player->programmed_action)
12806   {
12807     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12808     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12809     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12810     int jx = player->jx, jy = player->jy;
12811     boolean player_is_moving_to_valid_field =
12812       (!player_is_snapping &&
12813        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12814         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12815     boolean player_can_fall_down = canFallDown(player);
12816
12817     if (player_can_fall_down &&
12818         !player_is_moving_to_valid_field)
12819       player->programmed_action = MV_DOWN;
12820   }
12821 }
12822
12823 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12824 {
12825   return CheckGravityMovement(player);
12826
12827   if (player->gravity && !player->programmed_action)
12828   {
12829     int jx = player->jx, jy = player->jy;
12830     boolean field_under_player_is_free =
12831       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12832     boolean player_is_standing_on_valid_field =
12833       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12834        (IS_WALKABLE(Tile[jx][jy]) &&
12835         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12836
12837     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12838       player->programmed_action = MV_DOWN;
12839   }
12840 }
12841
12842 /*
12843   MovePlayerOneStep()
12844   -----------------------------------------------------------------------------
12845   dx, dy:               direction (non-diagonal) to try to move the player to
12846   real_dx, real_dy:     direction as read from input device (can be diagonal)
12847 */
12848
12849 boolean MovePlayerOneStep(struct PlayerInfo *player,
12850                           int dx, int dy, int real_dx, int real_dy)
12851 {
12852   int jx = player->jx, jy = player->jy;
12853   int new_jx = jx + dx, new_jy = jy + dy;
12854   int can_move;
12855   boolean player_can_move = !player->cannot_move;
12856
12857   if (!player->active || (!dx && !dy))
12858     return MP_NO_ACTION;
12859
12860   player->MovDir = (dx < 0 ? MV_LEFT :
12861                     dx > 0 ? MV_RIGHT :
12862                     dy < 0 ? MV_UP :
12863                     dy > 0 ? MV_DOWN :  MV_NONE);
12864
12865   if (!IN_LEV_FIELD(new_jx, new_jy))
12866     return MP_NO_ACTION;
12867
12868   if (!player_can_move)
12869   {
12870     if (player->MovPos == 0)
12871     {
12872       player->is_moving = FALSE;
12873       player->is_digging = FALSE;
12874       player->is_collecting = FALSE;
12875       player->is_snapping = FALSE;
12876       player->is_pushing = FALSE;
12877     }
12878   }
12879
12880   if (!network.enabled && game.centered_player_nr == -1 &&
12881       !AllPlayersInSight(player, new_jx, new_jy))
12882     return MP_NO_ACTION;
12883
12884   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx, real_dy, DF_DIG);
12885   if (can_move != MP_MOVING)
12886     return can_move;
12887
12888   // check if DigField() has caused relocation of the player
12889   if (player->jx != jx || player->jy != jy)
12890     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12891
12892   StorePlayer[jx][jy] = 0;
12893   player->last_jx = jx;
12894   player->last_jy = jy;
12895   player->jx = new_jx;
12896   player->jy = new_jy;
12897   StorePlayer[new_jx][new_jy] = player->element_nr;
12898
12899   if (player->move_delay_value_next != -1)
12900   {
12901     player->move_delay_value = player->move_delay_value_next;
12902     player->move_delay_value_next = -1;
12903   }
12904
12905   player->MovPos =
12906     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12907
12908   player->step_counter++;
12909
12910   PlayerVisit[jx][jy] = FrameCounter;
12911
12912   player->is_moving = TRUE;
12913
12914 #if 1
12915   // should better be called in MovePlayer(), but this breaks some tapes
12916   ScrollPlayer(player, SCROLL_INIT);
12917 #endif
12918
12919   return MP_MOVING;
12920 }
12921
12922 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12923 {
12924   int jx = player->jx, jy = player->jy;
12925   int old_jx = jx, old_jy = jy;
12926   int moved = MP_NO_ACTION;
12927
12928   if (!player->active)
12929     return FALSE;
12930
12931   if (!dx && !dy)
12932   {
12933     if (player->MovPos == 0)
12934     {
12935       player->is_moving = FALSE;
12936       player->is_digging = FALSE;
12937       player->is_collecting = FALSE;
12938       player->is_snapping = FALSE;
12939       player->is_pushing = FALSE;
12940     }
12941
12942     return FALSE;
12943   }
12944
12945   if (player->move_delay > 0)
12946     return FALSE;
12947
12948   player->move_delay = -1;              // set to "uninitialized" value
12949
12950   // store if player is automatically moved to next field
12951   player->is_auto_moving = (player->programmed_action != MV_NONE);
12952
12953   // remove the last programmed player action
12954   player->programmed_action = 0;
12955
12956   if (player->MovPos)
12957   {
12958     // should only happen if pre-1.2 tape recordings are played
12959     // this is only for backward compatibility
12960
12961     int original_move_delay_value = player->move_delay_value;
12962
12963 #if DEBUG
12964     Debug("game:playing:MovePlayer",
12965           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12966           tape.counter);
12967 #endif
12968
12969     // scroll remaining steps with finest movement resolution
12970     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12971
12972     while (player->MovPos)
12973     {
12974       ScrollPlayer(player, SCROLL_GO_ON);
12975       ScrollScreen(NULL, SCROLL_GO_ON);
12976
12977       AdvanceFrameAndPlayerCounters(player->index_nr);
12978
12979       DrawAllPlayers();
12980       BackToFront_WithFrameDelay(0);
12981     }
12982
12983     player->move_delay_value = original_move_delay_value;
12984   }
12985
12986   player->is_active = FALSE;
12987
12988   if (player->last_move_dir & MV_HORIZONTAL)
12989   {
12990     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12991       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12992   }
12993   else
12994   {
12995     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12996       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12997   }
12998
12999   if (!moved && !player->is_active)
13000   {
13001     player->is_moving = FALSE;
13002     player->is_digging = FALSE;
13003     player->is_collecting = FALSE;
13004     player->is_snapping = FALSE;
13005     player->is_pushing = FALSE;
13006   }
13007
13008   jx = player->jx;
13009   jy = player->jy;
13010
13011   if (moved & MP_MOVING && !ScreenMovPos &&
13012       (player->index_nr == game.centered_player_nr ||
13013        game.centered_player_nr == -1))
13014   {
13015     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13016
13017     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13018     {
13019       // actual player has left the screen -- scroll in that direction
13020       if (jx != old_jx)         // player has moved horizontally
13021         scroll_x += (jx - old_jx);
13022       else                      // player has moved vertically
13023         scroll_y += (jy - old_jy);
13024     }
13025     else
13026     {
13027       int offset_raw = game.scroll_delay_value;
13028
13029       if (jx != old_jx)         // player has moved horizontally
13030       {
13031         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
13032         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
13033         int new_scroll_x = jx - MIDPOSX + offset_x;
13034
13035         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
13036             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
13037           scroll_x = new_scroll_x;
13038
13039         // don't scroll over playfield boundaries
13040         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
13041
13042         // don't scroll more than one field at a time
13043         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13044
13045         // don't scroll against the player's moving direction
13046         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13047             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13048           scroll_x = old_scroll_x;
13049       }
13050       else                      // player has moved vertically
13051       {
13052         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
13053         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
13054         int new_scroll_y = jy - MIDPOSY + offset_y;
13055
13056         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
13057             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13058           scroll_y = new_scroll_y;
13059
13060         // don't scroll over playfield boundaries
13061         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13062
13063         // don't scroll more than one field at a time
13064         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13065
13066         // don't scroll against the player's moving direction
13067         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13068             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13069           scroll_y = old_scroll_y;
13070       }
13071     }
13072
13073     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13074     {
13075       if (!network.enabled && game.centered_player_nr == -1 &&
13076           !AllPlayersInVisibleScreen())
13077       {
13078         scroll_x = old_scroll_x;
13079         scroll_y = old_scroll_y;
13080       }
13081       else
13082       {
13083         ScrollScreen(player, SCROLL_INIT);
13084         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13085       }
13086     }
13087   }
13088
13089   player->StepFrame = 0;
13090
13091   if (moved & MP_MOVING)
13092   {
13093     if (old_jx != jx && old_jy == jy)
13094       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13095     else if (old_jx == jx && old_jy != jy)
13096       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13097
13098     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13099
13100     player->last_move_dir = player->MovDir;
13101     player->is_moving = TRUE;
13102     player->is_snapping = FALSE;
13103     player->is_switching = FALSE;
13104     player->is_dropping = FALSE;
13105     player->is_dropping_pressed = FALSE;
13106     player->drop_pressed_delay = 0;
13107
13108 #if 0
13109     // should better be called here than above, but this breaks some tapes
13110     ScrollPlayer(player, SCROLL_INIT);
13111 #endif
13112   }
13113   else
13114   {
13115     CheckGravityMovementWhenNotMoving(player);
13116
13117     player->is_moving = FALSE;
13118
13119     /* at this point, the player is allowed to move, but cannot move right now
13120        (e.g. because of something blocking the way) -- ensure that the player
13121        is also allowed to move in the next frame (in old versions before 3.1.1,
13122        the player was forced to wait again for eight frames before next try) */
13123
13124     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13125       player->move_delay = 0;   // allow direct movement in the next frame
13126   }
13127
13128   if (player->move_delay == -1)         // not yet initialized by DigField()
13129     player->move_delay = player->move_delay_value;
13130
13131   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13132   {
13133     TestIfPlayerTouchesBadThing(jx, jy);
13134     TestIfPlayerTouchesCustomElement(jx, jy);
13135   }
13136
13137   if (!player->active)
13138     RemovePlayer(player);
13139
13140   return moved;
13141 }
13142
13143 void ScrollPlayer(struct PlayerInfo *player, int mode)
13144 {
13145   int jx = player->jx, jy = player->jy;
13146   int last_jx = player->last_jx, last_jy = player->last_jy;
13147   int move_stepsize = TILEX / player->move_delay_value;
13148
13149   if (!player->active)
13150     return;
13151
13152   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13153     return;
13154
13155   if (mode == SCROLL_INIT)
13156   {
13157     player->actual_frame_counter.count = FrameCounter;
13158     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13159
13160     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13161         Tile[last_jx][last_jy] == EL_EMPTY)
13162     {
13163       int last_field_block_delay = 0;   // start with no blocking at all
13164       int block_delay_adjustment = player->block_delay_adjustment;
13165
13166       // if player blocks last field, add delay for exactly one move
13167       if (player->block_last_field)
13168       {
13169         last_field_block_delay += player->move_delay_value;
13170
13171         // when blocking enabled, prevent moving up despite gravity
13172         if (player->gravity && player->MovDir == MV_UP)
13173           block_delay_adjustment = -1;
13174       }
13175
13176       // add block delay adjustment (also possible when not blocking)
13177       last_field_block_delay += block_delay_adjustment;
13178
13179       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13180       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13181     }
13182
13183     if (player->MovPos != 0)    // player has not yet reached destination
13184       return;
13185   }
13186   else if (!FrameReached(&player->actual_frame_counter))
13187     return;
13188
13189   if (player->MovPos != 0)
13190   {
13191     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13192     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13193
13194     // before DrawPlayer() to draw correct player graphic for this case
13195     if (player->MovPos == 0)
13196       CheckGravityMovement(player);
13197   }
13198
13199   if (player->MovPos == 0)      // player reached destination field
13200   {
13201     if (player->move_delay_reset_counter > 0)
13202     {
13203       player->move_delay_reset_counter--;
13204
13205       if (player->move_delay_reset_counter == 0)
13206       {
13207         // continue with normal speed after quickly moving through gate
13208         HALVE_PLAYER_SPEED(player);
13209
13210         // be able to make the next move without delay
13211         player->move_delay = 0;
13212       }
13213     }
13214
13215     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13216         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13217         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13218         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13219         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13220         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13221         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13222         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13223     {
13224       ExitPlayer(player);
13225
13226       if (game.players_still_needed == 0 &&
13227           (game.friends_still_needed == 0 ||
13228            IS_SP_ELEMENT(Tile[jx][jy])))
13229         LevelSolved();
13230     }
13231
13232     player->last_jx = jx;
13233     player->last_jy = jy;
13234
13235     // this breaks one level: "machine", level 000
13236     {
13237       int move_direction = player->MovDir;
13238       int enter_side = MV_DIR_OPPOSITE(move_direction);
13239       int leave_side = move_direction;
13240       int old_jx = last_jx;
13241       int old_jy = last_jy;
13242       int old_element = Tile[old_jx][old_jy];
13243       int new_element = Tile[jx][jy];
13244
13245       if (IS_CUSTOM_ELEMENT(old_element))
13246         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13247                                    CE_LEFT_BY_PLAYER,
13248                                    player->index_bit, leave_side);
13249
13250       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13251                                           CE_PLAYER_LEAVES_X,
13252                                           player->index_bit, leave_side);
13253
13254       // needed because pushed element has not yet reached its destination,
13255       // so it would trigger a change event at its previous field location
13256       if (!player->is_pushing)
13257       {
13258         if (IS_CUSTOM_ELEMENT(new_element))
13259           CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13260                                      player->index_bit, enter_side);
13261
13262         CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13263                                             CE_PLAYER_ENTERS_X,
13264                                             player->index_bit, enter_side);
13265       }
13266
13267       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13268                                         CE_MOVE_OF_X, move_direction);
13269     }
13270
13271     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13272     {
13273       TestIfPlayerTouchesBadThing(jx, jy);
13274       TestIfPlayerTouchesCustomElement(jx, jy);
13275
13276       // needed because pushed element has not yet reached its destination,
13277       // so it would trigger a change event at its previous field location
13278       if (!player->is_pushing)
13279         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13280
13281       if (level.finish_dig_collect &&
13282           (player->is_digging || player->is_collecting))
13283       {
13284         int last_element = player->last_removed_element;
13285         int move_direction = player->MovDir;
13286         int enter_side = MV_DIR_OPPOSITE(move_direction);
13287         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13288                             CE_PLAYER_COLLECTS_X);
13289
13290         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13291                                             player->index_bit, enter_side);
13292
13293         player->last_removed_element = EL_UNDEFINED;
13294       }
13295
13296       if (!player->active)
13297         RemovePlayer(player);
13298     }
13299
13300     if (level.use_step_counter)
13301       CheckLevelTime_StepCounter();
13302
13303     if (tape.single_step && tape.recording && !tape.pausing &&
13304         !player->programmed_action)
13305       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13306
13307     if (!player->programmed_action)
13308       CheckSaveEngineSnapshot(player);
13309   }
13310 }
13311
13312 void ScrollScreen(struct PlayerInfo *player, int mode)
13313 {
13314   static DelayCounter screen_frame_counter = { 0 };
13315
13316   if (mode == SCROLL_INIT)
13317   {
13318     // set scrolling step size according to actual player's moving speed
13319     ScrollStepSize = TILEX / player->move_delay_value;
13320
13321     screen_frame_counter.count = FrameCounter;
13322     screen_frame_counter.value = 1;
13323
13324     ScreenMovDir = player->MovDir;
13325     ScreenMovPos = player->MovPos;
13326     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13327     return;
13328   }
13329   else if (!FrameReached(&screen_frame_counter))
13330     return;
13331
13332   if (ScreenMovPos)
13333   {
13334     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13335     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13336     redraw_mask |= REDRAW_FIELD;
13337   }
13338   else
13339     ScreenMovDir = MV_NONE;
13340 }
13341
13342 void CheckNextToConditions(int x, int y)
13343 {
13344   int element = Tile[x][y];
13345
13346   if (IS_PLAYER(x, y))
13347     TestIfPlayerNextToCustomElement(x, y);
13348
13349   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13350       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13351     TestIfElementNextToCustomElement(x, y);
13352 }
13353
13354 void TestIfPlayerNextToCustomElement(int x, int y)
13355 {
13356   struct XY *xy = xy_topdown;
13357   static int trigger_sides[4][2] =
13358   {
13359     // center side       border side
13360     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13361     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13362     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13363     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13364   };
13365   int i;
13366
13367   if (!IS_PLAYER(x, y))
13368     return;
13369
13370   struct PlayerInfo *player = PLAYERINFO(x, y);
13371
13372   if (player->is_moving)
13373     return;
13374
13375   for (i = 0; i < NUM_DIRECTIONS; i++)
13376   {
13377     int xx = x + xy[i].x;
13378     int yy = y + xy[i].y;
13379     int border_side = trigger_sides[i][1];
13380     int border_element;
13381
13382     if (!IN_LEV_FIELD(xx, yy))
13383       continue;
13384
13385     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13386       continue;         // center and border element not connected
13387
13388     border_element = Tile[xx][yy];
13389
13390     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13391                                player->index_bit, border_side);
13392     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13393                                         CE_PLAYER_NEXT_TO_X,
13394                                         player->index_bit, border_side);
13395
13396     /* use player element that is initially defined in the level playfield,
13397        not the player element that corresponds to the runtime player number
13398        (example: a level that contains EL_PLAYER_3 as the only player would
13399        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13400
13401     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13402                              CE_NEXT_TO_X, border_side);
13403   }
13404 }
13405
13406 void TestIfPlayerTouchesCustomElement(int x, int y)
13407 {
13408   struct XY *xy = xy_topdown;
13409   static int trigger_sides[4][2] =
13410   {
13411     // center side       border side
13412     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13413     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13414     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13415     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13416   };
13417   static int touch_dir[4] =
13418   {
13419     MV_LEFT | MV_RIGHT,
13420     MV_UP   | MV_DOWN,
13421     MV_UP   | MV_DOWN,
13422     MV_LEFT | MV_RIGHT
13423   };
13424   int center_element = Tile[x][y];      // should always be non-moving!
13425   int i;
13426
13427   for (i = 0; i < NUM_DIRECTIONS; i++)
13428   {
13429     int xx = x + xy[i].x;
13430     int yy = y + xy[i].y;
13431     int center_side = trigger_sides[i][0];
13432     int border_side = trigger_sides[i][1];
13433     int border_element;
13434
13435     if (!IN_LEV_FIELD(xx, yy))
13436       continue;
13437
13438     if (IS_PLAYER(x, y))                // player found at center element
13439     {
13440       struct PlayerInfo *player = PLAYERINFO(x, y);
13441
13442       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13443         border_element = Tile[xx][yy];          // may be moving!
13444       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13445         border_element = Tile[xx][yy];
13446       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13447         border_element = MovingOrBlocked2Element(xx, yy);
13448       else
13449         continue;               // center and border element do not touch
13450
13451       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13452                                  player->index_bit, border_side);
13453       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13454                                           CE_PLAYER_TOUCHES_X,
13455                                           player->index_bit, border_side);
13456
13457       {
13458         /* use player element that is initially defined in the level playfield,
13459            not the player element that corresponds to the runtime player number
13460            (example: a level that contains EL_PLAYER_3 as the only player would
13461            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13462         int player_element = PLAYERINFO(x, y)->initial_element;
13463
13464         CheckElementChangeBySide(xx, yy, border_element, player_element,
13465                                  CE_TOUCHING_X, border_side);
13466       }
13467     }
13468     else if (IS_PLAYER(xx, yy))         // player found at border element
13469     {
13470       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13471
13472       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13473       {
13474         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13475           continue;             // center and border element do not touch
13476       }
13477
13478       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13479                                  player->index_bit, center_side);
13480       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13481                                           CE_PLAYER_TOUCHES_X,
13482                                           player->index_bit, center_side);
13483
13484       {
13485         /* use player element that is initially defined in the level playfield,
13486            not the player element that corresponds to the runtime player number
13487            (example: a level that contains EL_PLAYER_3 as the only player would
13488            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13489         int player_element = PLAYERINFO(xx, yy)->initial_element;
13490
13491         CheckElementChangeBySide(x, y, center_element, player_element,
13492                                  CE_TOUCHING_X, center_side);
13493       }
13494
13495       break;
13496     }
13497   }
13498 }
13499
13500 void TestIfElementNextToCustomElement(int x, int y)
13501 {
13502   struct XY *xy = xy_topdown;
13503   static int trigger_sides[4][2] =
13504   {
13505     // center side      border side
13506     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13507     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13508     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13509     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13510   };
13511   int center_element = Tile[x][y];      // should always be non-moving!
13512   int i;
13513
13514   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13515     return;
13516
13517   for (i = 0; i < NUM_DIRECTIONS; i++)
13518   {
13519     int xx = x + xy[i].x;
13520     int yy = y + xy[i].y;
13521     int border_side = trigger_sides[i][1];
13522     int border_element;
13523
13524     if (!IN_LEV_FIELD(xx, yy))
13525       continue;
13526
13527     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13528       continue;                 // center and border element not connected
13529
13530     border_element = Tile[xx][yy];
13531
13532     // check for change of center element (but change it only once)
13533     if (CheckElementChangeBySide(x, y, center_element, border_element,
13534                                  CE_NEXT_TO_X, border_side))
13535       break;
13536   }
13537 }
13538
13539 void TestIfElementTouchesCustomElement(int x, int y)
13540 {
13541   struct XY *xy = xy_topdown;
13542   static int trigger_sides[4][2] =
13543   {
13544     // center side      border side
13545     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13546     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13547     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13548     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13549   };
13550   static int touch_dir[4] =
13551   {
13552     MV_LEFT | MV_RIGHT,
13553     MV_UP   | MV_DOWN,
13554     MV_UP   | MV_DOWN,
13555     MV_LEFT | MV_RIGHT
13556   };
13557   boolean change_center_element = FALSE;
13558   int center_element = Tile[x][y];      // should always be non-moving!
13559   int border_element_old[NUM_DIRECTIONS];
13560   int i;
13561
13562   for (i = 0; i < NUM_DIRECTIONS; i++)
13563   {
13564     int xx = x + xy[i].x;
13565     int yy = y + xy[i].y;
13566     int border_element;
13567
13568     border_element_old[i] = -1;
13569
13570     if (!IN_LEV_FIELD(xx, yy))
13571       continue;
13572
13573     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13574       border_element = Tile[xx][yy];    // may be moving!
13575     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13576       border_element = Tile[xx][yy];
13577     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13578       border_element = MovingOrBlocked2Element(xx, yy);
13579     else
13580       continue;                 // center and border element do not touch
13581
13582     border_element_old[i] = border_element;
13583   }
13584
13585   for (i = 0; i < NUM_DIRECTIONS; i++)
13586   {
13587     int xx = x + xy[i].x;
13588     int yy = y + xy[i].y;
13589     int center_side = trigger_sides[i][0];
13590     int border_element = border_element_old[i];
13591
13592     if (border_element == -1)
13593       continue;
13594
13595     // check for change of border element
13596     CheckElementChangeBySide(xx, yy, border_element, center_element,
13597                              CE_TOUCHING_X, center_side);
13598
13599     // (center element cannot be player, so we dont have to check this here)
13600   }
13601
13602   for (i = 0; i < NUM_DIRECTIONS; i++)
13603   {
13604     int xx = x + xy[i].x;
13605     int yy = y + xy[i].y;
13606     int border_side = trigger_sides[i][1];
13607     int border_element = border_element_old[i];
13608
13609     if (border_element == -1)
13610       continue;
13611
13612     // check for change of center element (but change it only once)
13613     if (!change_center_element)
13614       change_center_element =
13615         CheckElementChangeBySide(x, y, center_element, border_element,
13616                                  CE_TOUCHING_X, border_side);
13617
13618     if (IS_PLAYER(xx, yy))
13619     {
13620       /* use player element that is initially defined in the level playfield,
13621          not the player element that corresponds to the runtime player number
13622          (example: a level that contains EL_PLAYER_3 as the only player would
13623          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13624       int player_element = PLAYERINFO(xx, yy)->initial_element;
13625
13626       CheckElementChangeBySide(x, y, center_element, player_element,
13627                                CE_TOUCHING_X, border_side);
13628     }
13629   }
13630 }
13631
13632 void TestIfElementHitsCustomElement(int x, int y, int direction)
13633 {
13634   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13635   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13636   int hitx = x + dx, hity = y + dy;
13637   int hitting_element = Tile[x][y];
13638   int touched_element;
13639
13640   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13641     return;
13642
13643   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13644                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13645
13646   if (IN_LEV_FIELD(hitx, hity))
13647   {
13648     int opposite_direction = MV_DIR_OPPOSITE(direction);
13649     int hitting_side = direction;
13650     int touched_side = opposite_direction;
13651     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13652                           MovDir[hitx][hity] != direction ||
13653                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13654
13655     object_hit = TRUE;
13656
13657     if (object_hit)
13658     {
13659       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13660                                CE_HITTING_X, touched_side);
13661
13662       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13663                                CE_HIT_BY_X, hitting_side);
13664
13665       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13666                                CE_HIT_BY_SOMETHING, opposite_direction);
13667
13668       if (IS_PLAYER(hitx, hity))
13669       {
13670         /* use player element that is initially defined in the level playfield,
13671            not the player element that corresponds to the runtime player number
13672            (example: a level that contains EL_PLAYER_3 as the only player would
13673            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13674         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13675
13676         CheckElementChangeBySide(x, y, hitting_element, player_element,
13677                                  CE_HITTING_X, touched_side);
13678       }
13679     }
13680   }
13681
13682   // "hitting something" is also true when hitting the playfield border
13683   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13684                            CE_HITTING_SOMETHING, direction);
13685 }
13686
13687 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13688 {
13689   int i, kill_x = -1, kill_y = -1;
13690
13691   int bad_element = -1;
13692   struct XY *test_xy = xy_topdown;
13693   static int test_dir[4] =
13694   {
13695     MV_UP,
13696     MV_LEFT,
13697     MV_RIGHT,
13698     MV_DOWN
13699   };
13700
13701   for (i = 0; i < NUM_DIRECTIONS; i++)
13702   {
13703     int test_x, test_y, test_move_dir, test_element;
13704
13705     test_x = good_x + test_xy[i].x;
13706     test_y = good_y + test_xy[i].y;
13707
13708     if (!IN_LEV_FIELD(test_x, test_y))
13709       continue;
13710
13711     test_move_dir =
13712       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13713
13714     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13715
13716     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13717        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13718     */
13719     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13720         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13721     {
13722       kill_x = test_x;
13723       kill_y = test_y;
13724       bad_element = test_element;
13725
13726       break;
13727     }
13728   }
13729
13730   if (kill_x != -1 || kill_y != -1)
13731   {
13732     if (IS_PLAYER(good_x, good_y))
13733     {
13734       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13735
13736       if (player->shield_deadly_time_left > 0 &&
13737           !IS_INDESTRUCTIBLE(bad_element))
13738         Bang(kill_x, kill_y);
13739       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13740         KillPlayer(player);
13741     }
13742     else
13743       Bang(good_x, good_y);
13744   }
13745 }
13746
13747 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13748 {
13749   int i, kill_x = -1, kill_y = -1;
13750   int bad_element = Tile[bad_x][bad_y];
13751   struct XY *test_xy = xy_topdown;
13752   static int touch_dir[4] =
13753   {
13754     MV_LEFT | MV_RIGHT,
13755     MV_UP   | MV_DOWN,
13756     MV_UP   | MV_DOWN,
13757     MV_LEFT | MV_RIGHT
13758   };
13759   static int test_dir[4] =
13760   {
13761     MV_UP,
13762     MV_LEFT,
13763     MV_RIGHT,
13764     MV_DOWN
13765   };
13766
13767   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13768     return;
13769
13770   for (i = 0; i < NUM_DIRECTIONS; i++)
13771   {
13772     int test_x, test_y, test_move_dir, test_element;
13773
13774     test_x = bad_x + test_xy[i].x;
13775     test_y = bad_y + test_xy[i].y;
13776
13777     if (!IN_LEV_FIELD(test_x, test_y))
13778       continue;
13779
13780     test_move_dir =
13781       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13782
13783     test_element = Tile[test_x][test_y];
13784
13785     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13786        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13787     */
13788     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13789         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13790     {
13791       // good thing is player or penguin that does not move away
13792       if (IS_PLAYER(test_x, test_y))
13793       {
13794         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13795
13796         if (bad_element == EL_ROBOT && player->is_moving)
13797           continue;     // robot does not kill player if he is moving
13798
13799         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13800         {
13801           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13802             continue;           // center and border element do not touch
13803         }
13804
13805         kill_x = test_x;
13806         kill_y = test_y;
13807
13808         break;
13809       }
13810       else if (test_element == EL_PENGUIN)
13811       {
13812         kill_x = test_x;
13813         kill_y = test_y;
13814
13815         break;
13816       }
13817     }
13818   }
13819
13820   if (kill_x != -1 || kill_y != -1)
13821   {
13822     if (IS_PLAYER(kill_x, kill_y))
13823     {
13824       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13825
13826       if (player->shield_deadly_time_left > 0 &&
13827           !IS_INDESTRUCTIBLE(bad_element))
13828         Bang(bad_x, bad_y);
13829       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13830         KillPlayer(player);
13831     }
13832     else
13833       Bang(kill_x, kill_y);
13834   }
13835 }
13836
13837 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13838 {
13839   int bad_element = Tile[bad_x][bad_y];
13840   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13841   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13842   int test_x = bad_x + dx, test_y = bad_y + dy;
13843   int test_move_dir, test_element;
13844   int kill_x = -1, kill_y = -1;
13845
13846   if (!IN_LEV_FIELD(test_x, test_y))
13847     return;
13848
13849   test_move_dir =
13850     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13851
13852   test_element = Tile[test_x][test_y];
13853
13854   if (test_move_dir != bad_move_dir)
13855   {
13856     // good thing can be player or penguin that does not move away
13857     if (IS_PLAYER(test_x, test_y))
13858     {
13859       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13860
13861       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13862          player as being hit when he is moving towards the bad thing, because
13863          the "get hit by" condition would be lost after the player stops) */
13864       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13865         return;         // player moves away from bad thing
13866
13867       kill_x = test_x;
13868       kill_y = test_y;
13869     }
13870     else if (test_element == EL_PENGUIN)
13871     {
13872       kill_x = test_x;
13873       kill_y = test_y;
13874     }
13875   }
13876
13877   if (kill_x != -1 || kill_y != -1)
13878   {
13879     if (IS_PLAYER(kill_x, kill_y))
13880     {
13881       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13882
13883       if (player->shield_deadly_time_left > 0 &&
13884           !IS_INDESTRUCTIBLE(bad_element))
13885         Bang(bad_x, bad_y);
13886       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13887         KillPlayer(player);
13888     }
13889     else
13890       Bang(kill_x, kill_y);
13891   }
13892 }
13893
13894 void TestIfPlayerTouchesBadThing(int x, int y)
13895 {
13896   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13897 }
13898
13899 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13900 {
13901   TestIfGoodThingHitsBadThing(x, y, move_dir);
13902 }
13903
13904 void TestIfBadThingTouchesPlayer(int x, int y)
13905 {
13906   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13907 }
13908
13909 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13910 {
13911   TestIfBadThingHitsGoodThing(x, y, move_dir);
13912 }
13913
13914 void TestIfFriendTouchesBadThing(int x, int y)
13915 {
13916   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13917 }
13918
13919 void TestIfBadThingTouchesFriend(int x, int y)
13920 {
13921   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13922 }
13923
13924 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13925 {
13926   int i, kill_x = bad_x, kill_y = bad_y;
13927   struct XY *xy = xy_topdown;
13928
13929   for (i = 0; i < NUM_DIRECTIONS; i++)
13930   {
13931     int x, y, element;
13932
13933     x = bad_x + xy[i].x;
13934     y = bad_y + xy[i].y;
13935     if (!IN_LEV_FIELD(x, y))
13936       continue;
13937
13938     element = Tile[x][y];
13939     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13940         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13941     {
13942       kill_x = x;
13943       kill_y = y;
13944       break;
13945     }
13946   }
13947
13948   if (kill_x != bad_x || kill_y != bad_y)
13949     Bang(bad_x, bad_y);
13950 }
13951
13952 void KillPlayer(struct PlayerInfo *player)
13953 {
13954   int jx = player->jx, jy = player->jy;
13955
13956   if (!player->active)
13957     return;
13958
13959 #if 0
13960   Debug("game:playing:KillPlayer",
13961         "0: killed == %d, active == %d, reanimated == %d",
13962         player->killed, player->active, player->reanimated);
13963 #endif
13964
13965   /* the following code was introduced to prevent an infinite loop when calling
13966      -> Bang()
13967      -> CheckTriggeredElementChangeExt()
13968      -> ExecuteCustomElementAction()
13969      -> KillPlayer()
13970      -> (infinitely repeating the above sequence of function calls)
13971      which occurs when killing the player while having a CE with the setting
13972      "kill player X when explosion of <player X>"; the solution using a new
13973      field "player->killed" was chosen for backwards compatibility, although
13974      clever use of the fields "player->active" etc. would probably also work */
13975 #if 1
13976   if (player->killed)
13977     return;
13978 #endif
13979
13980   player->killed = TRUE;
13981
13982   // remove accessible field at the player's position
13983   RemoveField(jx, jy);
13984
13985   // deactivate shield (else Bang()/Explode() would not work right)
13986   player->shield_normal_time_left = 0;
13987   player->shield_deadly_time_left = 0;
13988
13989 #if 0
13990   Debug("game:playing:KillPlayer",
13991         "1: killed == %d, active == %d, reanimated == %d",
13992         player->killed, player->active, player->reanimated);
13993 #endif
13994
13995   Bang(jx, jy);
13996
13997 #if 0
13998   Debug("game:playing:KillPlayer",
13999         "2: killed == %d, active == %d, reanimated == %d",
14000         player->killed, player->active, player->reanimated);
14001 #endif
14002
14003   if (player->reanimated)       // killed player may have been reanimated
14004     player->killed = player->reanimated = FALSE;
14005   else
14006     BuryPlayer(player);
14007 }
14008
14009 static void KillPlayerUnlessEnemyProtected(int x, int y)
14010 {
14011   if (!PLAYER_ENEMY_PROTECTED(x, y))
14012     KillPlayer(PLAYERINFO(x, y));
14013 }
14014
14015 static void KillPlayerUnlessExplosionProtected(int x, int y)
14016 {
14017   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14018     KillPlayer(PLAYERINFO(x, y));
14019 }
14020
14021 void BuryPlayer(struct PlayerInfo *player)
14022 {
14023   int jx = player->jx, jy = player->jy;
14024
14025   if (!player->active)
14026     return;
14027
14028   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14029
14030   RemovePlayer(player);
14031
14032   player->buried = TRUE;
14033
14034   if (game.all_players_gone)
14035     game.GameOver = TRUE;
14036 }
14037
14038 void RemovePlayer(struct PlayerInfo *player)
14039 {
14040   int jx = player->jx, jy = player->jy;
14041   int i, found = FALSE;
14042
14043   player->present = FALSE;
14044   player->active = FALSE;
14045
14046   // required for some CE actions (even if the player is not active anymore)
14047   player->MovPos = 0;
14048
14049   if (!ExplodeField[jx][jy])
14050     StorePlayer[jx][jy] = 0;
14051
14052   if (player->is_moving)
14053     TEST_DrawLevelField(player->last_jx, player->last_jy);
14054
14055   for (i = 0; i < MAX_PLAYERS; i++)
14056     if (stored_player[i].active)
14057       found = TRUE;
14058
14059   if (!found)
14060   {
14061     game.all_players_gone = TRUE;
14062     game.GameOver = TRUE;
14063   }
14064
14065   game.exit_x = game.robot_wheel_x = jx;
14066   game.exit_y = game.robot_wheel_y = jy;
14067 }
14068
14069 void ExitPlayer(struct PlayerInfo *player)
14070 {
14071   DrawPlayer(player);   // needed here only to cleanup last field
14072   RemovePlayer(player);
14073
14074   if (game.players_still_needed > 0)
14075     game.players_still_needed--;
14076 }
14077
14078 static void SetFieldForSnapping(int x, int y, int element, int direction,
14079                                 int player_index_bit)
14080 {
14081   struct ElementInfo *ei = &element_info[element];
14082   int direction_bit = MV_DIR_TO_BIT(direction);
14083   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14084   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14085                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14086
14087   Tile[x][y] = EL_ELEMENT_SNAPPING;
14088   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14089   MovDir[x][y] = direction;
14090   Store[x][y] = element;
14091   Store2[x][y] = player_index_bit;
14092
14093   ResetGfxAnimation(x, y);
14094
14095   GfxElement[x][y] = element;
14096   GfxAction[x][y] = action;
14097   GfxDir[x][y] = direction;
14098   GfxFrame[x][y] = -1;
14099 }
14100
14101 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14102                                    int player_index_bit)
14103 {
14104   TestIfElementTouchesCustomElement(x, y);      // for empty space
14105
14106   if (level.finish_dig_collect)
14107   {
14108     int dig_side = MV_DIR_OPPOSITE(direction);
14109     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14110                         CE_PLAYER_COLLECTS_X);
14111
14112     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14113                                         player_index_bit, dig_side);
14114     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14115                                         player_index_bit, dig_side);
14116   }
14117 }
14118
14119 /*
14120   =============================================================================
14121   checkDiagonalPushing()
14122   -----------------------------------------------------------------------------
14123   check if diagonal input device direction results in pushing of object
14124   (by checking if the alternative direction is walkable, diggable, ...)
14125   =============================================================================
14126 */
14127
14128 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14129                                     int x, int y, int real_dx, int real_dy)
14130 {
14131   int jx, jy, dx, dy, xx, yy;
14132
14133   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14134     return TRUE;
14135
14136   // diagonal direction: check alternative direction
14137   jx = player->jx;
14138   jy = player->jy;
14139   dx = x - jx;
14140   dy = y - jy;
14141   xx = jx + (dx == 0 ? real_dx : 0);
14142   yy = jy + (dy == 0 ? real_dy : 0);
14143
14144   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14145 }
14146
14147 /*
14148   =============================================================================
14149   DigField()
14150   -----------------------------------------------------------------------------
14151   x, y:                 field next to player (non-diagonal) to try to dig to
14152   real_dx, real_dy:     direction as read from input device (can be diagonal)
14153   =============================================================================
14154 */
14155
14156 static int DigField(struct PlayerInfo *player,
14157                     int oldx, int oldy, int x, int y,
14158                     int real_dx, int real_dy, int mode)
14159 {
14160   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14161   boolean player_was_pushing = player->is_pushing;
14162   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14163   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14164   int jx = oldx, jy = oldy;
14165   int dx = x - jx, dy = y - jy;
14166   int nextx = x + dx, nexty = y + dy;
14167   int move_direction = (dx == -1 ? MV_LEFT  :
14168                         dx == +1 ? MV_RIGHT :
14169                         dy == -1 ? MV_UP    :
14170                         dy == +1 ? MV_DOWN  : MV_NONE);
14171   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14172   int dig_side = MV_DIR_OPPOSITE(move_direction);
14173   int old_element = Tile[jx][jy];
14174   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14175   int collect_count;
14176
14177   if (is_player)                // function can also be called by EL_PENGUIN
14178   {
14179     if (player->MovPos == 0)
14180     {
14181       player->is_digging = FALSE;
14182       player->is_collecting = FALSE;
14183     }
14184
14185     if (player->MovPos == 0)    // last pushing move finished
14186       player->is_pushing = FALSE;
14187
14188     if (mode == DF_NO_PUSH)     // player just stopped pushing
14189     {
14190       player->is_switching = FALSE;
14191       player->push_delay = -1;
14192
14193       return MP_NO_ACTION;
14194     }
14195   }
14196   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14197     old_element = Back[jx][jy];
14198
14199   // in case of element dropped at player position, check background
14200   else if (Back[jx][jy] != EL_EMPTY &&
14201            game.engine_version >= VERSION_IDENT(2,2,0,0))
14202     old_element = Back[jx][jy];
14203
14204   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14205     return MP_NO_ACTION;        // field has no opening in this direction
14206
14207   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element, opposite_direction))
14208     return MP_NO_ACTION;        // field has no opening in this direction
14209
14210   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14211   {
14212     SplashAcid(x, y);
14213
14214     Tile[jx][jy] = player->artwork_element;
14215     InitMovingField(jx, jy, MV_DOWN);
14216     Store[jx][jy] = EL_ACID;
14217     ContinueMoving(jx, jy);
14218     BuryPlayer(player);
14219
14220     return MP_DONT_RUN_INTO;
14221   }
14222
14223   if (player_can_move && DONT_RUN_INTO(element))
14224   {
14225     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14226
14227     return MP_DONT_RUN_INTO;
14228   }
14229
14230   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14231     return MP_NO_ACTION;
14232
14233   collect_count = element_info[element].collect_count_initial;
14234
14235   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14236     return MP_NO_ACTION;
14237
14238   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14239     player_can_move = player_can_move_or_snap;
14240
14241   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14242       game.engine_version >= VERSION_IDENT(2,2,0,0))
14243   {
14244     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14245                                player->index_bit, dig_side);
14246     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14247                                         player->index_bit, dig_side);
14248
14249     if (element == EL_DC_LANDMINE)
14250       Bang(x, y);
14251
14252     if (Tile[x][y] != element)          // field changed by snapping
14253       return MP_ACTION;
14254
14255     return MP_NO_ACTION;
14256   }
14257
14258   if (player->gravity && is_player && !player->is_auto_moving &&
14259       canFallDown(player) && move_direction != MV_DOWN &&
14260       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14261     return MP_NO_ACTION;        // player cannot walk here due to gravity
14262
14263   if (player_can_move &&
14264       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14265   {
14266     int sound_element = SND_ELEMENT(element);
14267     int sound_action = ACTION_WALKING;
14268
14269     if (IS_RND_GATE(element))
14270     {
14271       if (!player->key[RND_GATE_NR(element)])
14272         return MP_NO_ACTION;
14273     }
14274     else if (IS_RND_GATE_GRAY(element))
14275     {
14276       if (!player->key[RND_GATE_GRAY_NR(element)])
14277         return MP_NO_ACTION;
14278     }
14279     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14280     {
14281       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14282         return MP_NO_ACTION;
14283     }
14284     else if (element == EL_EXIT_OPEN ||
14285              element == EL_EM_EXIT_OPEN ||
14286              element == EL_EM_EXIT_OPENING ||
14287              element == EL_STEEL_EXIT_OPEN ||
14288              element == EL_EM_STEEL_EXIT_OPEN ||
14289              element == EL_EM_STEEL_EXIT_OPENING ||
14290              element == EL_SP_EXIT_OPEN ||
14291              element == EL_SP_EXIT_OPENING)
14292     {
14293       sound_action = ACTION_PASSING;    // player is passing exit
14294     }
14295     else if (element == EL_EMPTY)
14296     {
14297       sound_action = ACTION_MOVING;             // nothing to walk on
14298     }
14299
14300     // play sound from background or player, whatever is available
14301     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14302       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14303     else
14304       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14305   }
14306   else if (player_can_move &&
14307            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14308   {
14309     if (!ACCESS_FROM(element, opposite_direction))
14310       return MP_NO_ACTION;      // field not accessible from this direction
14311
14312     if (CAN_MOVE(element))      // only fixed elements can be passed!
14313       return MP_NO_ACTION;
14314
14315     if (IS_EM_GATE(element))
14316     {
14317       if (!player->key[EM_GATE_NR(element)])
14318         return MP_NO_ACTION;
14319     }
14320     else if (IS_EM_GATE_GRAY(element))
14321     {
14322       if (!player->key[EM_GATE_GRAY_NR(element)])
14323         return MP_NO_ACTION;
14324     }
14325     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14326     {
14327       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14328         return MP_NO_ACTION;
14329     }
14330     else if (IS_EMC_GATE(element))
14331     {
14332       if (!player->key[EMC_GATE_NR(element)])
14333         return MP_NO_ACTION;
14334     }
14335     else if (IS_EMC_GATE_GRAY(element))
14336     {
14337       if (!player->key[EMC_GATE_GRAY_NR(element)])
14338         return MP_NO_ACTION;
14339     }
14340     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14341     {
14342       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14343         return MP_NO_ACTION;
14344     }
14345     else if (element == EL_DC_GATE_WHITE ||
14346              element == EL_DC_GATE_WHITE_GRAY ||
14347              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14348     {
14349       if (player->num_white_keys == 0)
14350         return MP_NO_ACTION;
14351
14352       player->num_white_keys--;
14353     }
14354     else if (IS_SP_PORT(element))
14355     {
14356       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14357           element == EL_SP_GRAVITY_PORT_RIGHT ||
14358           element == EL_SP_GRAVITY_PORT_UP ||
14359           element == EL_SP_GRAVITY_PORT_DOWN)
14360         player->gravity = !player->gravity;
14361       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14362                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14363                element == EL_SP_GRAVITY_ON_PORT_UP ||
14364                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14365         player->gravity = TRUE;
14366       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14367                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14368                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14369                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14370         player->gravity = FALSE;
14371     }
14372
14373     // automatically move to the next field with double speed
14374     player->programmed_action = move_direction;
14375
14376     if (player->move_delay_reset_counter == 0)
14377     {
14378       player->move_delay_reset_counter = 2;     // two double speed steps
14379
14380       DOUBLE_PLAYER_SPEED(player);
14381     }
14382
14383     PlayLevelSoundAction(x, y, ACTION_PASSING);
14384   }
14385   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14386   {
14387     RemoveField(x, y);
14388
14389     if (mode != DF_SNAP)
14390     {
14391       GfxElement[x][y] = GFX_ELEMENT(element);
14392       player->is_digging = TRUE;
14393     }
14394
14395     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14396
14397     // use old behaviour for old levels (digging)
14398     if (!level.finish_dig_collect)
14399     {
14400       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14401                                           player->index_bit, dig_side);
14402
14403       // if digging triggered player relocation, finish digging tile
14404       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14405         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14406     }
14407
14408     if (mode == DF_SNAP)
14409     {
14410       if (level.block_snap_field)
14411         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14412       else
14413         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14414
14415       // use old behaviour for old levels (snapping)
14416       if (!level.finish_dig_collect)
14417         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14418                                             player->index_bit, dig_side);
14419     }
14420   }
14421   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14422   {
14423     RemoveField(x, y);
14424
14425     if (is_player && mode != DF_SNAP)
14426     {
14427       GfxElement[x][y] = element;
14428       player->is_collecting = TRUE;
14429     }
14430
14431     if (element == EL_SPEED_PILL)
14432     {
14433       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14434     }
14435     else if (element == EL_EXTRA_TIME && level.time > 0)
14436     {
14437       TimeLeft += level.extra_time;
14438
14439       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14440
14441       DisplayGameControlValues();
14442     }
14443     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14444     {
14445       int shield_time = (element == EL_SHIELD_DEADLY ?
14446                          level.shield_deadly_time :
14447                          level.shield_normal_time);
14448
14449       player->shield_normal_time_left += shield_time;
14450       if (element == EL_SHIELD_DEADLY)
14451         player->shield_deadly_time_left += shield_time;
14452     }
14453     else if (element == EL_DYNAMITE ||
14454              element == EL_EM_DYNAMITE ||
14455              element == EL_SP_DISK_RED)
14456     {
14457       if (player->inventory_size < MAX_INVENTORY_SIZE)
14458         player->inventory_element[player->inventory_size++] = element;
14459
14460       DrawGameDoorValues();
14461     }
14462     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14463     {
14464       player->dynabomb_count++;
14465       player->dynabombs_left++;
14466     }
14467     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14468     {
14469       player->dynabomb_size++;
14470     }
14471     else if (element == EL_DYNABOMB_INCREASE_POWER)
14472     {
14473       player->dynabomb_xl = TRUE;
14474     }
14475     else if (IS_KEY(element))
14476     {
14477       player->key[KEY_NR(element)] = TRUE;
14478
14479       DrawGameDoorValues();
14480     }
14481     else if (element == EL_DC_KEY_WHITE)
14482     {
14483       player->num_white_keys++;
14484
14485       // display white keys?
14486       // DrawGameDoorValues();
14487     }
14488     else if (IS_ENVELOPE(element))
14489     {
14490       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14491
14492       if (!wait_for_snapping)
14493         player->show_envelope = element;
14494     }
14495     else if (element == EL_EMC_LENSES)
14496     {
14497       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14498
14499       RedrawAllInvisibleElementsForLenses();
14500     }
14501     else if (element == EL_EMC_MAGNIFIER)
14502     {
14503       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14504
14505       RedrawAllInvisibleElementsForMagnifier();
14506     }
14507     else if (IS_DROPPABLE(element) ||
14508              IS_THROWABLE(element))     // can be collected and dropped
14509     {
14510       int i;
14511
14512       if (collect_count == 0)
14513         player->inventory_infinite_element = element;
14514       else
14515         for (i = 0; i < collect_count; i++)
14516           if (player->inventory_size < MAX_INVENTORY_SIZE)
14517             player->inventory_element[player->inventory_size++] = element;
14518
14519       DrawGameDoorValues();
14520     }
14521     else if (collect_count > 0)
14522     {
14523       game.gems_still_needed -= collect_count;
14524       if (game.gems_still_needed < 0)
14525         game.gems_still_needed = 0;
14526
14527       game.snapshot.collected_item = TRUE;
14528
14529       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14530
14531       DisplayGameControlValues();
14532     }
14533
14534     RaiseScoreElement(element);
14535     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14536
14537     // use old behaviour for old levels (collecting)
14538     if (!level.finish_dig_collect && is_player)
14539     {
14540       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14541                                           player->index_bit, dig_side);
14542
14543       // if collecting triggered player relocation, finish collecting tile
14544       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14545         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14546     }
14547
14548     if (mode == DF_SNAP)
14549     {
14550       if (level.block_snap_field)
14551         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14552       else
14553         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14554
14555       // use old behaviour for old levels (snapping)
14556       if (!level.finish_dig_collect)
14557         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14558                                             player->index_bit, dig_side);
14559     }
14560   }
14561   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14562   {
14563     if (mode == DF_SNAP && element != EL_BD_ROCK)
14564       return MP_NO_ACTION;
14565
14566     if (CAN_FALL(element) && dy)
14567       return MP_NO_ACTION;
14568
14569     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14570         !(element == EL_SPRING && level.use_spring_bug))
14571       return MP_NO_ACTION;
14572
14573     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14574         ((move_direction & MV_VERTICAL &&
14575           ((element_info[element].move_pattern & MV_LEFT &&
14576             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14577            (element_info[element].move_pattern & MV_RIGHT &&
14578             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14579          (move_direction & MV_HORIZONTAL &&
14580           ((element_info[element].move_pattern & MV_UP &&
14581             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14582            (element_info[element].move_pattern & MV_DOWN &&
14583             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14584       return MP_NO_ACTION;
14585
14586     // do not push elements already moving away faster than player
14587     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14588         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14589       return MP_NO_ACTION;
14590
14591     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14592     {
14593       if (player->push_delay_value == -1 || !player_was_pushing)
14594         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14595     }
14596     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14597     {
14598       if (player->push_delay_value == -1)
14599         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14600     }
14601     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14602     {
14603       if (!player->is_pushing)
14604         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14605     }
14606
14607     player->is_pushing = TRUE;
14608     player->is_active = TRUE;
14609
14610     if (!(IN_LEV_FIELD(nextx, nexty) &&
14611           (IS_FREE(nextx, nexty) ||
14612            (IS_SB_ELEMENT(element) &&
14613             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14614            (IS_CUSTOM_ELEMENT(element) &&
14615             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14616       return MP_NO_ACTION;
14617
14618     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14619       return MP_NO_ACTION;
14620
14621     if (player->push_delay == -1)       // new pushing; restart delay
14622       player->push_delay = 0;
14623
14624     if (player->push_delay < player->push_delay_value &&
14625         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14626         element != EL_SPRING && element != EL_BALLOON)
14627     {
14628       // make sure that there is no move delay before next try to push
14629       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14630         player->move_delay = 0;
14631
14632       return MP_NO_ACTION;
14633     }
14634
14635     if (IS_CUSTOM_ELEMENT(element) &&
14636         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14637     {
14638       if (!DigFieldByCE(nextx, nexty, element))
14639         return MP_NO_ACTION;
14640     }
14641
14642     if (IS_SB_ELEMENT(element))
14643     {
14644       boolean sokoban_task_solved = FALSE;
14645
14646       if (element == EL_SOKOBAN_FIELD_FULL)
14647       {
14648         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14649
14650         IncrementSokobanFieldsNeeded();
14651         IncrementSokobanObjectsNeeded();
14652       }
14653
14654       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14655       {
14656         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14657
14658         DecrementSokobanFieldsNeeded();
14659         DecrementSokobanObjectsNeeded();
14660
14661         // sokoban object was pushed from empty field to sokoban field
14662         if (Back[x][y] == EL_EMPTY)
14663           sokoban_task_solved = TRUE;
14664       }
14665
14666       Tile[x][y] = EL_SOKOBAN_OBJECT;
14667
14668       if (Back[x][y] == Back[nextx][nexty])
14669         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14670       else if (Back[x][y] != 0)
14671         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14672                                     ACTION_EMPTYING);
14673       else
14674         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14675                                     ACTION_FILLING);
14676
14677       if (sokoban_task_solved &&
14678           game.sokoban_fields_still_needed == 0 &&
14679           game.sokoban_objects_still_needed == 0 &&
14680           level.auto_exit_sokoban)
14681       {
14682         game.players_still_needed = 0;
14683
14684         LevelSolved();
14685
14686         PlaySound(SND_GAME_SOKOBAN_SOLVING);
14687       }
14688     }
14689     else
14690       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14691
14692     InitMovingField(x, y, move_direction);
14693     GfxAction[x][y] = ACTION_PUSHING;
14694
14695     if (mode == DF_SNAP)
14696       ContinueMoving(x, y);
14697     else
14698       MovPos[x][y] = (dx != 0 ? dx : dy);
14699
14700     Pushed[x][y] = TRUE;
14701     Pushed[nextx][nexty] = TRUE;
14702
14703     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14704       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14705     else
14706       player->push_delay_value = -1;    // get new value later
14707
14708     // check for element change _after_ element has been pushed
14709     if (game.use_change_when_pushing_bug)
14710     {
14711       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14712                                  player->index_bit, dig_side);
14713       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14714                                           player->index_bit, dig_side);
14715     }
14716   }
14717   else if (IS_SWITCHABLE(element))
14718   {
14719     if (PLAYER_SWITCHING(player, x, y))
14720     {
14721       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14722                                           player->index_bit, dig_side);
14723
14724       return MP_ACTION;
14725     }
14726
14727     player->is_switching = TRUE;
14728     player->switch_x = x;
14729     player->switch_y = y;
14730
14731     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14732
14733     if (element == EL_ROBOT_WHEEL)
14734     {
14735       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14736
14737       game.robot_wheel_x = x;
14738       game.robot_wheel_y = y;
14739       game.robot_wheel_active = TRUE;
14740
14741       TEST_DrawLevelField(x, y);
14742     }
14743     else if (element == EL_SP_TERMINAL)
14744     {
14745       int xx, yy;
14746
14747       SCAN_PLAYFIELD(xx, yy)
14748       {
14749         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14750         {
14751           Bang(xx, yy);
14752         }
14753         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14754         {
14755           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14756
14757           ResetGfxAnimation(xx, yy);
14758           TEST_DrawLevelField(xx, yy);
14759         }
14760       }
14761     }
14762     else if (IS_BELT_SWITCH(element))
14763     {
14764       ToggleBeltSwitch(x, y);
14765     }
14766     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14767              element == EL_SWITCHGATE_SWITCH_DOWN ||
14768              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14769              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14770     {
14771       ToggleSwitchgateSwitch();
14772     }
14773     else if (element == EL_LIGHT_SWITCH ||
14774              element == EL_LIGHT_SWITCH_ACTIVE)
14775     {
14776       ToggleLightSwitch(x, y);
14777     }
14778     else if (element == EL_TIMEGATE_SWITCH ||
14779              element == EL_DC_TIMEGATE_SWITCH)
14780     {
14781       ActivateTimegateSwitch(x, y);
14782     }
14783     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14784              element == EL_BALLOON_SWITCH_RIGHT ||
14785              element == EL_BALLOON_SWITCH_UP    ||
14786              element == EL_BALLOON_SWITCH_DOWN  ||
14787              element == EL_BALLOON_SWITCH_NONE  ||
14788              element == EL_BALLOON_SWITCH_ANY)
14789     {
14790       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14791                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14792                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14793                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14794                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14795                              move_direction);
14796     }
14797     else if (element == EL_LAMP)
14798     {
14799       Tile[x][y] = EL_LAMP_ACTIVE;
14800       game.lights_still_needed--;
14801
14802       ResetGfxAnimation(x, y);
14803       TEST_DrawLevelField(x, y);
14804     }
14805     else if (element == EL_TIME_ORB_FULL)
14806     {
14807       Tile[x][y] = EL_TIME_ORB_EMPTY;
14808
14809       if (level.time > 0 || level.use_time_orb_bug)
14810       {
14811         TimeLeft += level.time_orb_time;
14812         game.no_level_time_limit = FALSE;
14813
14814         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14815
14816         DisplayGameControlValues();
14817       }
14818
14819       ResetGfxAnimation(x, y);
14820       TEST_DrawLevelField(x, y);
14821     }
14822     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14823              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14824     {
14825       int xx, yy;
14826
14827       game.ball_active = !game.ball_active;
14828
14829       SCAN_PLAYFIELD(xx, yy)
14830       {
14831         int e = Tile[xx][yy];
14832
14833         if (game.ball_active)
14834         {
14835           if (e == EL_EMC_MAGIC_BALL)
14836             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14837           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14838             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14839         }
14840         else
14841         {
14842           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14843             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14844           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14845             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14846         }
14847       }
14848     }
14849
14850     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14851                                         player->index_bit, dig_side);
14852
14853     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14854                                         player->index_bit, dig_side);
14855
14856     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14857                                         player->index_bit, dig_side);
14858
14859     return MP_ACTION;
14860   }
14861   else
14862   {
14863     if (!PLAYER_SWITCHING(player, x, y))
14864     {
14865       player->is_switching = TRUE;
14866       player->switch_x = x;
14867       player->switch_y = y;
14868
14869       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14870                                  player->index_bit, dig_side);
14871       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14872                                           player->index_bit, dig_side);
14873
14874       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14875                                  player->index_bit, dig_side);
14876       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14877                                           player->index_bit, dig_side);
14878     }
14879
14880     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14881                                player->index_bit, dig_side);
14882     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14883                                         player->index_bit, dig_side);
14884
14885     return MP_NO_ACTION;
14886   }
14887
14888   player->push_delay = -1;
14889
14890   if (is_player)                // function can also be called by EL_PENGUIN
14891   {
14892     if (Tile[x][y] != element)          // really digged/collected something
14893     {
14894       player->is_collecting = !player->is_digging;
14895       player->is_active = TRUE;
14896
14897       player->last_removed_element = element;
14898     }
14899   }
14900
14901   return MP_MOVING;
14902 }
14903
14904 static boolean DigFieldByCE(int x, int y, int digging_element)
14905 {
14906   int element = Tile[x][y];
14907
14908   if (!IS_FREE(x, y))
14909   {
14910     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14911                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14912                   ACTION_BREAKING);
14913
14914     // no element can dig solid indestructible elements
14915     if (IS_INDESTRUCTIBLE(element) &&
14916         !IS_DIGGABLE(element) &&
14917         !IS_COLLECTIBLE(element))
14918       return FALSE;
14919
14920     if (AmoebaNr[x][y] &&
14921         (element == EL_AMOEBA_FULL ||
14922          element == EL_BD_AMOEBA ||
14923          element == EL_AMOEBA_GROWING))
14924     {
14925       AmoebaCnt[AmoebaNr[x][y]]--;
14926       AmoebaCnt2[AmoebaNr[x][y]]--;
14927     }
14928
14929     if (IS_MOVING(x, y))
14930       RemoveMovingField(x, y);
14931     else
14932     {
14933       RemoveField(x, y);
14934       TEST_DrawLevelField(x, y);
14935     }
14936
14937     // if digged element was about to explode, prevent the explosion
14938     ExplodeField[x][y] = EX_TYPE_NONE;
14939
14940     PlayLevelSoundAction(x, y, action);
14941   }
14942
14943   Store[x][y] = EL_EMPTY;
14944
14945   // this makes it possible to leave the removed element again
14946   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14947     Store[x][y] = element;
14948
14949   return TRUE;
14950 }
14951
14952 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14953 {
14954   int jx = player->jx, jy = player->jy;
14955   int x = jx + dx, y = jy + dy;
14956   int snap_direction = (dx == -1 ? MV_LEFT  :
14957                         dx == +1 ? MV_RIGHT :
14958                         dy == -1 ? MV_UP    :
14959                         dy == +1 ? MV_DOWN  : MV_NONE);
14960   boolean can_continue_snapping = (level.continuous_snapping &&
14961                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14962
14963   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14964     return FALSE;
14965
14966   if (!player->active || !IN_LEV_FIELD(x, y))
14967     return FALSE;
14968
14969   if (dx && dy)
14970     return FALSE;
14971
14972   if (!dx && !dy)
14973   {
14974     if (player->MovPos == 0)
14975       player->is_pushing = FALSE;
14976
14977     player->is_snapping = FALSE;
14978
14979     if (player->MovPos == 0)
14980     {
14981       player->is_moving = FALSE;
14982       player->is_digging = FALSE;
14983       player->is_collecting = FALSE;
14984     }
14985
14986     return FALSE;
14987   }
14988
14989   // prevent snapping with already pressed snap key when not allowed
14990   if (player->is_snapping && !can_continue_snapping)
14991     return FALSE;
14992
14993   player->MovDir = snap_direction;
14994
14995   if (player->MovPos == 0)
14996   {
14997     player->is_moving = FALSE;
14998     player->is_digging = FALSE;
14999     player->is_collecting = FALSE;
15000   }
15001
15002   player->is_dropping = FALSE;
15003   player->is_dropping_pressed = FALSE;
15004   player->drop_pressed_delay = 0;
15005
15006   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15007     return FALSE;
15008
15009   player->is_snapping = TRUE;
15010   player->is_active = TRUE;
15011
15012   if (player->MovPos == 0)
15013   {
15014     player->is_moving = FALSE;
15015     player->is_digging = FALSE;
15016     player->is_collecting = FALSE;
15017   }
15018
15019   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
15020     TEST_DrawLevelField(player->last_jx, player->last_jy);
15021
15022   TEST_DrawLevelField(x, y);
15023
15024   return TRUE;
15025 }
15026
15027 static boolean DropElement(struct PlayerInfo *player)
15028 {
15029   int old_element, new_element;
15030   int dropx = player->jx, dropy = player->jy;
15031   int drop_direction = player->MovDir;
15032   int drop_side = drop_direction;
15033   int drop_element = get_next_dropped_element(player);
15034
15035   /* do not drop an element on top of another element; when holding drop key
15036      pressed without moving, dropped element must move away before the next
15037      element can be dropped (this is especially important if the next element
15038      is dynamite, which can be placed on background for historical reasons) */
15039   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
15040     return MP_ACTION;
15041
15042   if (IS_THROWABLE(drop_element))
15043   {
15044     dropx += GET_DX_FROM_DIR(drop_direction);
15045     dropy += GET_DY_FROM_DIR(drop_direction);
15046
15047     if (!IN_LEV_FIELD(dropx, dropy))
15048       return FALSE;
15049   }
15050
15051   old_element = Tile[dropx][dropy];     // old element at dropping position
15052   new_element = drop_element;           // default: no change when dropping
15053
15054   // check if player is active, not moving and ready to drop
15055   if (!player->active || player->MovPos || player->drop_delay > 0)
15056     return FALSE;
15057
15058   // check if player has anything that can be dropped
15059   if (new_element == EL_UNDEFINED)
15060     return FALSE;
15061
15062   // only set if player has anything that can be dropped
15063   player->is_dropping_pressed = TRUE;
15064
15065   // check if drop key was pressed long enough for EM style dynamite
15066   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15067     return FALSE;
15068
15069   // check if anything can be dropped at the current position
15070   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15071     return FALSE;
15072
15073   // collected custom elements can only be dropped on empty fields
15074   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15075     return FALSE;
15076
15077   if (old_element != EL_EMPTY)
15078     Back[dropx][dropy] = old_element;   // store old element on this field
15079
15080   ResetGfxAnimation(dropx, dropy);
15081   ResetRandomAnimationValue(dropx, dropy);
15082
15083   if (player->inventory_size > 0 ||
15084       player->inventory_infinite_element != EL_UNDEFINED)
15085   {
15086     if (player->inventory_size > 0)
15087     {
15088       player->inventory_size--;
15089
15090       DrawGameDoorValues();
15091
15092       if (new_element == EL_DYNAMITE)
15093         new_element = EL_DYNAMITE_ACTIVE;
15094       else if (new_element == EL_EM_DYNAMITE)
15095         new_element = EL_EM_DYNAMITE_ACTIVE;
15096       else if (new_element == EL_SP_DISK_RED)
15097         new_element = EL_SP_DISK_RED_ACTIVE;
15098     }
15099
15100     Tile[dropx][dropy] = new_element;
15101
15102     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15103       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15104                           el2img(Tile[dropx][dropy]), 0);
15105
15106     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15107
15108     // needed if previous element just changed to "empty" in the last frame
15109     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15110
15111     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15112                                player->index_bit, drop_side);
15113     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15114                                         CE_PLAYER_DROPS_X,
15115                                         player->index_bit, drop_side);
15116
15117     TestIfElementTouchesCustomElement(dropx, dropy);
15118   }
15119   else          // player is dropping a dyna bomb
15120   {
15121     player->dynabombs_left--;
15122
15123     Tile[dropx][dropy] = new_element;
15124
15125     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15126       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15127                           el2img(Tile[dropx][dropy]), 0);
15128
15129     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15130   }
15131
15132   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15133     InitField_WithBug1(dropx, dropy, FALSE);
15134
15135   new_element = Tile[dropx][dropy];     // element might have changed
15136
15137   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15138       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15139   {
15140     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15141       MovDir[dropx][dropy] = drop_direction;
15142
15143     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15144
15145     // do not cause impact style collision by dropping elements that can fall
15146     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15147   }
15148
15149   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15150   player->is_dropping = TRUE;
15151
15152   player->drop_pressed_delay = 0;
15153   player->is_dropping_pressed = FALSE;
15154
15155   player->drop_x = dropx;
15156   player->drop_y = dropy;
15157
15158   return TRUE;
15159 }
15160
15161 // ----------------------------------------------------------------------------
15162 // game sound playing functions
15163 // ----------------------------------------------------------------------------
15164
15165 static int *loop_sound_frame = NULL;
15166 static int *loop_sound_volume = NULL;
15167
15168 void InitPlayLevelSound(void)
15169 {
15170   int num_sounds = getSoundListSize();
15171
15172   checked_free(loop_sound_frame);
15173   checked_free(loop_sound_volume);
15174
15175   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15176   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15177 }
15178
15179 static void PlayLevelSound(int x, int y, int nr)
15180 {
15181   int sx = SCREENX(x), sy = SCREENY(y);
15182   int volume, stereo_position;
15183   int max_distance = 8;
15184   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15185
15186   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15187       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15188     return;
15189
15190   if (!IN_LEV_FIELD(x, y) ||
15191       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15192       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15193     return;
15194
15195   volume = SOUND_MAX_VOLUME;
15196
15197   if (!IN_SCR_FIELD(sx, sy))
15198   {
15199     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15200     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15201
15202     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15203   }
15204
15205   stereo_position = (SOUND_MAX_LEFT +
15206                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15207                      (SCR_FIELDX + 2 * max_distance));
15208
15209   if (IS_LOOP_SOUND(nr))
15210   {
15211     /* This assures that quieter loop sounds do not overwrite louder ones,
15212        while restarting sound volume comparison with each new game frame. */
15213
15214     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15215       return;
15216
15217     loop_sound_volume[nr] = volume;
15218     loop_sound_frame[nr] = FrameCounter;
15219   }
15220
15221   PlaySoundExt(nr, volume, stereo_position, type);
15222 }
15223
15224 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15225 {
15226   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15227                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15228                  y < LEVELY(BY1) ? LEVELY(BY1) :
15229                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15230                  sound_action);
15231 }
15232
15233 static void PlayLevelSoundAction(int x, int y, int action)
15234 {
15235   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15236 }
15237
15238 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15239 {
15240   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15241
15242   if (sound_effect != SND_UNDEFINED)
15243     PlayLevelSound(x, y, sound_effect);
15244 }
15245
15246 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15247                                               int action)
15248 {
15249   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15250
15251   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15252     PlayLevelSound(x, y, sound_effect);
15253 }
15254
15255 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15256 {
15257   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15258
15259   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15260     PlayLevelSound(x, y, sound_effect);
15261 }
15262
15263 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15264 {
15265   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15266
15267   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15268     StopSound(sound_effect);
15269 }
15270
15271 static int getLevelMusicNr(void)
15272 {
15273   int level_pos = level_nr - leveldir_current->first_level;
15274
15275   if (levelset.music[level_nr] != MUS_UNDEFINED)
15276     return levelset.music[level_nr];            // from config file
15277   else
15278     return MAP_NOCONF_MUSIC(level_pos);         // from music dir
15279 }
15280
15281 static void FadeLevelSounds(void)
15282 {
15283   FadeSounds();
15284 }
15285
15286 static void FadeLevelMusic(void)
15287 {
15288   int music_nr = getLevelMusicNr();
15289   char *curr_music = getCurrentlyPlayingMusicFilename();
15290   char *next_music = getMusicInfoEntryFilename(music_nr);
15291
15292   if (!strEqual(curr_music, next_music))
15293     FadeMusic();
15294 }
15295
15296 void FadeLevelSoundsAndMusic(void)
15297 {
15298   FadeLevelSounds();
15299   FadeLevelMusic();
15300 }
15301
15302 static void PlayLevelMusic(void)
15303 {
15304   int music_nr = getLevelMusicNr();
15305   char *curr_music = getCurrentlyPlayingMusicFilename();
15306   char *next_music = getMusicInfoEntryFilename(music_nr);
15307
15308   if (!strEqual(curr_music, next_music))
15309     PlayMusicLoop(music_nr);
15310 }
15311
15312 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15313 {
15314   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15315   int offset = 0;
15316   int x = xx - offset;
15317   int y = yy - offset;
15318
15319   switch (sample)
15320   {
15321     case SOUND_blank:
15322       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15323       break;
15324
15325     case SOUND_roll:
15326       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15327       break;
15328
15329     case SOUND_stone:
15330       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15331       break;
15332
15333     case SOUND_nut:
15334       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15335       break;
15336
15337     case SOUND_crack:
15338       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15339       break;
15340
15341     case SOUND_bug:
15342       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15343       break;
15344
15345     case SOUND_tank:
15346       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15347       break;
15348
15349     case SOUND_android_clone:
15350       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15351       break;
15352
15353     case SOUND_android_move:
15354       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15355       break;
15356
15357     case SOUND_spring:
15358       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15359       break;
15360
15361     case SOUND_slurp:
15362       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15363       break;
15364
15365     case SOUND_eater:
15366       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15367       break;
15368
15369     case SOUND_eater_eat:
15370       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15371       break;
15372
15373     case SOUND_alien:
15374       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15375       break;
15376
15377     case SOUND_collect:
15378       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15379       break;
15380
15381     case SOUND_diamond:
15382       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15383       break;
15384
15385     case SOUND_squash:
15386       // !!! CHECK THIS !!!
15387 #if 1
15388       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15389 #else
15390       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15391 #endif
15392       break;
15393
15394     case SOUND_wonderfall:
15395       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15396       break;
15397
15398     case SOUND_drip:
15399       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15400       break;
15401
15402     case SOUND_push:
15403       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15404       break;
15405
15406     case SOUND_dirt:
15407       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15408       break;
15409
15410     case SOUND_acid:
15411       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15412       break;
15413
15414     case SOUND_ball:
15415       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15416       break;
15417
15418     case SOUND_slide:
15419       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15420       break;
15421
15422     case SOUND_wonder:
15423       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15424       break;
15425
15426     case SOUND_door:
15427       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15428       break;
15429
15430     case SOUND_exit_open:
15431       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15432       break;
15433
15434     case SOUND_exit_leave:
15435       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15436       break;
15437
15438     case SOUND_dynamite:
15439       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15440       break;
15441
15442     case SOUND_tick:
15443       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15444       break;
15445
15446     case SOUND_press:
15447       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15448       break;
15449
15450     case SOUND_wheel:
15451       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15452       break;
15453
15454     case SOUND_boom:
15455       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15456       break;
15457
15458     case SOUND_die:
15459       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15460       break;
15461
15462     case SOUND_time:
15463       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15464       break;
15465
15466     default:
15467       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15468       break;
15469   }
15470 }
15471
15472 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15473 {
15474   int element = map_element_SP_to_RND(element_sp);
15475   int action = map_action_SP_to_RND(action_sp);
15476   int offset = (setup.sp_show_border_elements ? 0 : 1);
15477   int x = xx - offset;
15478   int y = yy - offset;
15479
15480   PlayLevelSoundElementAction(x, y, element, action);
15481 }
15482
15483 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15484 {
15485   int element = map_element_MM_to_RND(element_mm);
15486   int action = map_action_MM_to_RND(action_mm);
15487   int offset = 0;
15488   int x = xx - offset;
15489   int y = yy - offset;
15490
15491   if (!IS_MM_ELEMENT(element))
15492     element = EL_MM_DEFAULT;
15493
15494   PlayLevelSoundElementAction(x, y, element, action);
15495 }
15496
15497 void PlaySound_MM(int sound_mm)
15498 {
15499   int sound = map_sound_MM_to_RND(sound_mm);
15500
15501   if (sound == SND_UNDEFINED)
15502     return;
15503
15504   PlaySound(sound);
15505 }
15506
15507 void PlaySoundLoop_MM(int sound_mm)
15508 {
15509   int sound = map_sound_MM_to_RND(sound_mm);
15510
15511   if (sound == SND_UNDEFINED)
15512     return;
15513
15514   PlaySoundLoop(sound);
15515 }
15516
15517 void StopSound_MM(int sound_mm)
15518 {
15519   int sound = map_sound_MM_to_RND(sound_mm);
15520
15521   if (sound == SND_UNDEFINED)
15522     return;
15523
15524   StopSound(sound);
15525 }
15526
15527 void RaiseScore(int value)
15528 {
15529   game.score += value;
15530
15531   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15532
15533   DisplayGameControlValues();
15534 }
15535
15536 void RaiseScoreElement(int element)
15537 {
15538   switch (element)
15539   {
15540     case EL_EMERALD:
15541     case EL_BD_DIAMOND:
15542     case EL_EMERALD_YELLOW:
15543     case EL_EMERALD_RED:
15544     case EL_EMERALD_PURPLE:
15545     case EL_SP_INFOTRON:
15546       RaiseScore(level.score[SC_EMERALD]);
15547       break;
15548     case EL_DIAMOND:
15549       RaiseScore(level.score[SC_DIAMOND]);
15550       break;
15551     case EL_CRYSTAL:
15552       RaiseScore(level.score[SC_CRYSTAL]);
15553       break;
15554     case EL_PEARL:
15555       RaiseScore(level.score[SC_PEARL]);
15556       break;
15557     case EL_BUG:
15558     case EL_BD_BUTTERFLY:
15559     case EL_SP_ELECTRON:
15560       RaiseScore(level.score[SC_BUG]);
15561       break;
15562     case EL_SPACESHIP:
15563     case EL_BD_FIREFLY:
15564     case EL_SP_SNIKSNAK:
15565       RaiseScore(level.score[SC_SPACESHIP]);
15566       break;
15567     case EL_YAMYAM:
15568     case EL_DARK_YAMYAM:
15569       RaiseScore(level.score[SC_YAMYAM]);
15570       break;
15571     case EL_ROBOT:
15572       RaiseScore(level.score[SC_ROBOT]);
15573       break;
15574     case EL_PACMAN:
15575       RaiseScore(level.score[SC_PACMAN]);
15576       break;
15577     case EL_NUT:
15578       RaiseScore(level.score[SC_NUT]);
15579       break;
15580     case EL_DYNAMITE:
15581     case EL_EM_DYNAMITE:
15582     case EL_SP_DISK_RED:
15583     case EL_DYNABOMB_INCREASE_NUMBER:
15584     case EL_DYNABOMB_INCREASE_SIZE:
15585     case EL_DYNABOMB_INCREASE_POWER:
15586       RaiseScore(level.score[SC_DYNAMITE]);
15587       break;
15588     case EL_SHIELD_NORMAL:
15589     case EL_SHIELD_DEADLY:
15590       RaiseScore(level.score[SC_SHIELD]);
15591       break;
15592     case EL_EXTRA_TIME:
15593       RaiseScore(level.extra_time_score);
15594       break;
15595     case EL_KEY_1:
15596     case EL_KEY_2:
15597     case EL_KEY_3:
15598     case EL_KEY_4:
15599     case EL_EM_KEY_1:
15600     case EL_EM_KEY_2:
15601     case EL_EM_KEY_3:
15602     case EL_EM_KEY_4:
15603     case EL_EMC_KEY_5:
15604     case EL_EMC_KEY_6:
15605     case EL_EMC_KEY_7:
15606     case EL_EMC_KEY_8:
15607     case EL_DC_KEY_WHITE:
15608       RaiseScore(level.score[SC_KEY]);
15609       break;
15610     default:
15611       RaiseScore(element_info[element].collect_score);
15612       break;
15613   }
15614 }
15615
15616 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15617 {
15618   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15619   {
15620     if (!quick_quit)
15621     {
15622       // prevent short reactivation of overlay buttons while closing door
15623       SetOverlayActive(FALSE);
15624       UnmapGameButtons();
15625
15626       // door may still be open due to skipped or envelope style request
15627       CloseDoor(score_info_tape_play ? DOOR_CLOSE_ALL : DOOR_CLOSE_1);
15628     }
15629
15630     if (network.enabled)
15631       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15632     else
15633     {
15634       if (quick_quit)
15635         FadeSkipNextFadeIn();
15636
15637       SetGameStatus(GAME_MODE_MAIN);
15638
15639       DrawMainMenu();
15640     }
15641   }
15642   else          // continue playing the game
15643   {
15644     if (tape.playing && tape.deactivate_display)
15645       TapeDeactivateDisplayOff(TRUE);
15646
15647     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15648
15649     if (tape.playing && tape.deactivate_display)
15650       TapeDeactivateDisplayOn();
15651   }
15652 }
15653
15654 void RequestQuitGame(boolean escape_key_pressed)
15655 {
15656   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15657   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15658                         level_editor_test_game);
15659   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15660                           quick_quit || score_info_tape_play);
15661
15662   RequestQuitGameExt(skip_request, quick_quit,
15663                      "Do you really want to quit the game?");
15664 }
15665
15666 static char *getRestartGameMessage(void)
15667 {
15668   boolean play_again = hasStartedNetworkGame();
15669   static char message[MAX_OUTPUT_LINESIZE];
15670   char *game_over_text = "Game over!";
15671   char *play_again_text = " Play it again?";
15672
15673   if (level.game_engine_type == GAME_ENGINE_TYPE_MM &&
15674       game_mm.game_over_message != NULL)
15675     game_over_text = game_mm.game_over_message;
15676
15677   snprintf(message, MAX_OUTPUT_LINESIZE, "%s%s", game_over_text,
15678            (play_again ? play_again_text : ""));
15679
15680   return message;
15681 }
15682
15683 static void RequestRestartGame(void)
15684 {
15685   char *message = getRestartGameMessage();
15686   boolean has_started_game = hasStartedNetworkGame();
15687   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15688   int door_state = DOOR_CLOSE_1;
15689
15690   if (Request(message, request_mode | REQ_STAY_OPEN) && has_started_game)
15691   {
15692     CloseDoor(door_state);
15693
15694     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15695   }
15696   else
15697   {
15698     // if game was invoked from level editor, also close tape recorder door
15699     if (level_editor_test_game)
15700       door_state = DOOR_CLOSE_ALL;
15701
15702     CloseDoor(door_state);
15703
15704     SetGameStatus(GAME_MODE_MAIN);
15705
15706     DrawMainMenu();
15707   }
15708 }
15709
15710 boolean CheckRestartGame(void)
15711 {
15712   static int game_over_delay = 0;
15713   int game_over_delay_value = 50;
15714   boolean game_over = checkGameFailed();
15715
15716   if (!game_over)
15717   {
15718     game_over_delay = game_over_delay_value;
15719
15720     return FALSE;
15721   }
15722
15723   if (game_over_delay > 0)
15724   {
15725     if (game_over_delay == game_over_delay_value / 2)
15726       PlaySound(SND_GAME_LOSING);
15727
15728     game_over_delay--;
15729
15730     return FALSE;
15731   }
15732
15733   // do not handle game over if request dialog is already active
15734   if (game.request_active)
15735     return FALSE;
15736
15737   // do not ask to play again if game was never actually played
15738   if (!game.GamePlayed)
15739     return FALSE;
15740
15741   // do not ask to play again if this was disabled in setup menu
15742   if (!setup.ask_on_game_over)
15743     return FALSE;
15744
15745   RequestRestartGame();
15746
15747   return TRUE;
15748 }
15749
15750 boolean checkGameSolved(void)
15751 {
15752   // set for all game engines if level was solved
15753   return game.LevelSolved_GameEnd;
15754 }
15755
15756 boolean checkGameFailed(void)
15757 {
15758   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15759     return (game_em.game_over && !game_em.level_solved);
15760   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15761     return (game_sp.game_over && !game_sp.level_solved);
15762   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15763     return (game_mm.game_over && !game_mm.level_solved);
15764   else                          // GAME_ENGINE_TYPE_RND
15765     return (game.GameOver && !game.LevelSolved);
15766 }
15767
15768 boolean checkGameEnded(void)
15769 {
15770   return (checkGameSolved() || checkGameFailed());
15771 }
15772
15773
15774 // ----------------------------------------------------------------------------
15775 // random generator functions
15776 // ----------------------------------------------------------------------------
15777
15778 unsigned int InitEngineRandom_RND(int seed)
15779 {
15780   game.num_random_calls = 0;
15781
15782   return InitEngineRandom(seed);
15783 }
15784
15785 unsigned int RND(int max)
15786 {
15787   if (max > 0)
15788   {
15789     game.num_random_calls++;
15790
15791     return GetEngineRandom(max);
15792   }
15793
15794   return 0;
15795 }
15796
15797
15798 // ----------------------------------------------------------------------------
15799 // game engine snapshot handling functions
15800 // ----------------------------------------------------------------------------
15801
15802 struct EngineSnapshotInfo
15803 {
15804   // runtime values for custom element collect score
15805   int collect_score[NUM_CUSTOM_ELEMENTS];
15806
15807   // runtime values for group element choice position
15808   int choice_pos[NUM_GROUP_ELEMENTS];
15809
15810   // runtime values for belt position animations
15811   int belt_graphic[4][NUM_BELT_PARTS];
15812   int belt_anim_mode[4][NUM_BELT_PARTS];
15813 };
15814
15815 static struct EngineSnapshotInfo engine_snapshot_rnd;
15816 static char *snapshot_level_identifier = NULL;
15817 static int snapshot_level_nr = -1;
15818
15819 static void SaveEngineSnapshotValues_RND(void)
15820 {
15821   static int belt_base_active_element[4] =
15822   {
15823     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15824     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15825     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15826     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15827   };
15828   int i, j;
15829
15830   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15831   {
15832     int element = EL_CUSTOM_START + i;
15833
15834     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15835   }
15836
15837   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15838   {
15839     int element = EL_GROUP_START + i;
15840
15841     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15842   }
15843
15844   for (i = 0; i < 4; i++)
15845   {
15846     for (j = 0; j < NUM_BELT_PARTS; j++)
15847     {
15848       int element = belt_base_active_element[i] + j;
15849       int graphic = el2img(element);
15850       int anim_mode = graphic_info[graphic].anim_mode;
15851
15852       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15853       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15854     }
15855   }
15856 }
15857
15858 static void LoadEngineSnapshotValues_RND(void)
15859 {
15860   unsigned int num_random_calls = game.num_random_calls;
15861   int i, j;
15862
15863   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15864   {
15865     int element = EL_CUSTOM_START + i;
15866
15867     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15868   }
15869
15870   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15871   {
15872     int element = EL_GROUP_START + i;
15873
15874     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15875   }
15876
15877   for (i = 0; i < 4; i++)
15878   {
15879     for (j = 0; j < NUM_BELT_PARTS; j++)
15880     {
15881       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15882       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15883
15884       graphic_info[graphic].anim_mode = anim_mode;
15885     }
15886   }
15887
15888   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15889   {
15890     InitRND(tape.random_seed);
15891     for (i = 0; i < num_random_calls; i++)
15892       RND(1);
15893   }
15894
15895   if (game.num_random_calls != num_random_calls)
15896   {
15897     Error("number of random calls out of sync");
15898     Error("number of random calls should be %d", num_random_calls);
15899     Error("number of random calls is %d", game.num_random_calls);
15900
15901     Fail("this should not happen -- please debug");
15902   }
15903 }
15904
15905 void FreeEngineSnapshotSingle(void)
15906 {
15907   FreeSnapshotSingle();
15908
15909   setString(&snapshot_level_identifier, NULL);
15910   snapshot_level_nr = -1;
15911 }
15912
15913 void FreeEngineSnapshotList(void)
15914 {
15915   FreeSnapshotList();
15916 }
15917
15918 static ListNode *SaveEngineSnapshotBuffers(void)
15919 {
15920   ListNode *buffers = NULL;
15921
15922   // copy some special values to a structure better suited for the snapshot
15923
15924   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15925     SaveEngineSnapshotValues_RND();
15926   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15927     SaveEngineSnapshotValues_EM();
15928   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15929     SaveEngineSnapshotValues_SP(&buffers);
15930   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15931     SaveEngineSnapshotValues_MM();
15932
15933   // save values stored in special snapshot structure
15934
15935   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15936     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15937   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15938     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15939   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15940     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15941   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15942     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15943
15944   // save further RND engine values
15945
15946   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15947   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15948   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15949
15950   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15951   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15952   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15953   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15954   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15955
15956   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15957   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15958   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15959
15960   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15961
15962   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15963   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15964
15965   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15966   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15967   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15968   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15969   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15970   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15971   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15972   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15973   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15974   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15975   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15976   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15977   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15978   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15979   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15980   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15981   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15982   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15983
15984   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15985   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15986
15987   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15988   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15989   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15990
15991   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15992   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15993
15994   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15995   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15996   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
15997   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15998   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15999   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16000
16001   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16002   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16003
16004 #if 0
16005   ListNode *node = engine_snapshot_list_rnd;
16006   int num_bytes = 0;
16007
16008   while (node != NULL)
16009   {
16010     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16011
16012     node = node->next;
16013   }
16014
16015   Debug("game:playing:SaveEngineSnapshotBuffers",
16016         "size of engine snapshot: %d bytes", num_bytes);
16017 #endif
16018
16019   return buffers;
16020 }
16021
16022 void SaveEngineSnapshotSingle(void)
16023 {
16024   ListNode *buffers = SaveEngineSnapshotBuffers();
16025
16026   // finally save all snapshot buffers to single snapshot
16027   SaveSnapshotSingle(buffers);
16028
16029   // save level identification information
16030   setString(&snapshot_level_identifier, leveldir_current->identifier);
16031   snapshot_level_nr = level_nr;
16032 }
16033
16034 boolean CheckSaveEngineSnapshotToList(void)
16035 {
16036   boolean save_snapshot =
16037     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
16038      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
16039       game.snapshot.changed_action) ||
16040      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16041       game.snapshot.collected_item));
16042
16043   game.snapshot.changed_action = FALSE;
16044   game.snapshot.collected_item = FALSE;
16045   game.snapshot.save_snapshot = save_snapshot;
16046
16047   return save_snapshot;
16048 }
16049
16050 void SaveEngineSnapshotToList(void)
16051 {
16052   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
16053       tape.quick_resume)
16054     return;
16055
16056   ListNode *buffers = SaveEngineSnapshotBuffers();
16057
16058   // finally save all snapshot buffers to snapshot list
16059   SaveSnapshotToList(buffers);
16060 }
16061
16062 void SaveEngineSnapshotToListInitial(void)
16063 {
16064   FreeEngineSnapshotList();
16065
16066   SaveEngineSnapshotToList();
16067 }
16068
16069 static void LoadEngineSnapshotValues(void)
16070 {
16071   // restore special values from snapshot structure
16072
16073   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16074     LoadEngineSnapshotValues_RND();
16075   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16076     LoadEngineSnapshotValues_EM();
16077   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16078     LoadEngineSnapshotValues_SP();
16079   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16080     LoadEngineSnapshotValues_MM();
16081 }
16082
16083 void LoadEngineSnapshotSingle(void)
16084 {
16085   LoadSnapshotSingle();
16086
16087   LoadEngineSnapshotValues();
16088 }
16089
16090 static void LoadEngineSnapshot_Undo(int steps)
16091 {
16092   LoadSnapshotFromList_Older(steps);
16093
16094   LoadEngineSnapshotValues();
16095 }
16096
16097 static void LoadEngineSnapshot_Redo(int steps)
16098 {
16099   LoadSnapshotFromList_Newer(steps);
16100
16101   LoadEngineSnapshotValues();
16102 }
16103
16104 boolean CheckEngineSnapshotSingle(void)
16105 {
16106   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16107           snapshot_level_nr == level_nr);
16108 }
16109
16110 boolean CheckEngineSnapshotList(void)
16111 {
16112   return CheckSnapshotList();
16113 }
16114
16115
16116 // ---------- new game button stuff -------------------------------------------
16117
16118 static struct
16119 {
16120   int graphic;
16121   struct XY *pos;
16122   int gadget_id;
16123   boolean *setup_value;
16124   boolean allowed_on_tape;
16125   boolean is_touch_button;
16126   char *infotext;
16127 } gamebutton_info[NUM_GAME_BUTTONS] =
16128 {
16129   {
16130     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16131     GAME_CTRL_ID_STOP,                          NULL,
16132     TRUE, FALSE,                                "stop game"
16133   },
16134   {
16135     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16136     GAME_CTRL_ID_PAUSE,                         NULL,
16137     TRUE, FALSE,                                "pause game"
16138   },
16139   {
16140     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16141     GAME_CTRL_ID_PLAY,                          NULL,
16142     TRUE, FALSE,                                "play game"
16143   },
16144   {
16145     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16146     GAME_CTRL_ID_UNDO,                          NULL,
16147     TRUE, FALSE,                                "undo step"
16148   },
16149   {
16150     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16151     GAME_CTRL_ID_REDO,                          NULL,
16152     TRUE, FALSE,                                "redo step"
16153   },
16154   {
16155     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16156     GAME_CTRL_ID_SAVE,                          NULL,
16157     TRUE, FALSE,                                "save game"
16158   },
16159   {
16160     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16161     GAME_CTRL_ID_PAUSE2,                        NULL,
16162     TRUE, FALSE,                                "pause game"
16163   },
16164   {
16165     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16166     GAME_CTRL_ID_LOAD,                          NULL,
16167     TRUE, FALSE,                                "load game"
16168   },
16169   {
16170     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16171     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16172     FALSE, FALSE,                               "stop game"
16173   },
16174   {
16175     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16176     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16177     FALSE, FALSE,                               "pause game"
16178   },
16179   {
16180     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16181     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16182     FALSE, FALSE,                               "play game"
16183   },
16184   {
16185     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16186     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16187     FALSE, TRUE,                                "stop game"
16188   },
16189   {
16190     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16191     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16192     FALSE, TRUE,                                "pause game"
16193   },
16194   {
16195     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16196     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16197     TRUE, FALSE,                                "background music on/off"
16198   },
16199   {
16200     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16201     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16202     TRUE, FALSE,                                "sound loops on/off"
16203   },
16204   {
16205     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16206     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16207     TRUE, FALSE,                                "normal sounds on/off"
16208   },
16209   {
16210     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16211     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16212     FALSE, FALSE,                               "background music on/off"
16213   },
16214   {
16215     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16216     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16217     FALSE, FALSE,                               "sound loops on/off"
16218   },
16219   {
16220     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16221     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16222     FALSE, FALSE,                               "normal sounds on/off"
16223   }
16224 };
16225
16226 void CreateGameButtons(void)
16227 {
16228   int i;
16229
16230   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16231   {
16232     int graphic = gamebutton_info[i].graphic;
16233     struct GraphicInfo *gfx = &graphic_info[graphic];
16234     struct XY *pos = gamebutton_info[i].pos;
16235     struct GadgetInfo *gi;
16236     int button_type;
16237     boolean checked;
16238     unsigned int event_mask;
16239     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16240     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16241     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16242     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16243     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16244     int gd_x   = gfx->src_x;
16245     int gd_y   = gfx->src_y;
16246     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16247     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16248     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16249     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16250     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16251     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16252     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16253     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16254     int id = i;
16255
16256     // do not use touch buttons if overlay touch buttons are disabled
16257     if (is_touch_button && !setup.touch.overlay_buttons)
16258       continue;
16259
16260     if (gfx->bitmap == NULL)
16261     {
16262       game_gadget[id] = NULL;
16263
16264       continue;
16265     }
16266
16267     if (id == GAME_CTRL_ID_STOP ||
16268         id == GAME_CTRL_ID_PANEL_STOP ||
16269         id == GAME_CTRL_ID_TOUCH_STOP ||
16270         id == GAME_CTRL_ID_PLAY ||
16271         id == GAME_CTRL_ID_PANEL_PLAY ||
16272         id == GAME_CTRL_ID_SAVE ||
16273         id == GAME_CTRL_ID_LOAD)
16274     {
16275       button_type = GD_TYPE_NORMAL_BUTTON;
16276       checked = FALSE;
16277       event_mask = GD_EVENT_RELEASED;
16278     }
16279     else if (id == GAME_CTRL_ID_UNDO ||
16280              id == GAME_CTRL_ID_REDO)
16281     {
16282       button_type = GD_TYPE_NORMAL_BUTTON;
16283       checked = FALSE;
16284       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16285     }
16286     else
16287     {
16288       button_type = GD_TYPE_CHECK_BUTTON;
16289       checked = (gamebutton_info[i].setup_value != NULL ?
16290                  *gamebutton_info[i].setup_value : FALSE);
16291       event_mask = GD_EVENT_PRESSED;
16292     }
16293
16294     gi = CreateGadget(GDI_CUSTOM_ID, id,
16295                       GDI_IMAGE_ID, graphic,
16296                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16297                       GDI_X, base_x + x,
16298                       GDI_Y, base_y + y,
16299                       GDI_WIDTH, gfx->width,
16300                       GDI_HEIGHT, gfx->height,
16301                       GDI_TYPE, button_type,
16302                       GDI_STATE, GD_BUTTON_UNPRESSED,
16303                       GDI_CHECKED, checked,
16304                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16305                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16306                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16307                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16308                       GDI_DIRECT_DRAW, FALSE,
16309                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16310                       GDI_EVENT_MASK, event_mask,
16311                       GDI_CALLBACK_ACTION, HandleGameButtons,
16312                       GDI_END);
16313
16314     if (gi == NULL)
16315       Fail("cannot create gadget");
16316
16317     game_gadget[id] = gi;
16318   }
16319 }
16320
16321 void FreeGameButtons(void)
16322 {
16323   int i;
16324
16325   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16326     FreeGadget(game_gadget[i]);
16327 }
16328
16329 static void UnmapGameButtonsAtSamePosition(int id)
16330 {
16331   int i;
16332
16333   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16334     if (i != id &&
16335         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16336         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16337       UnmapGadget(game_gadget[i]);
16338 }
16339
16340 static void UnmapGameButtonsAtSamePosition_All(void)
16341 {
16342   if (setup.show_load_save_buttons)
16343   {
16344     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16345     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16346     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16347   }
16348   else if (setup.show_undo_redo_buttons)
16349   {
16350     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16351     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16352     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16353   }
16354   else
16355   {
16356     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16357     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16358     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16359
16360     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16361     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16362     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16363   }
16364 }
16365
16366 void MapLoadSaveButtons(void)
16367 {
16368   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16369   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16370
16371   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
16372   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
16373 }
16374
16375 void MapUndoRedoButtons(void)
16376 {
16377   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16378   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16379
16380   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16381   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16382 }
16383
16384 void ModifyPauseButtons(void)
16385 {
16386   static int ids[] =
16387   {
16388     GAME_CTRL_ID_PAUSE,
16389     GAME_CTRL_ID_PAUSE2,
16390     GAME_CTRL_ID_PANEL_PAUSE,
16391     GAME_CTRL_ID_TOUCH_PAUSE,
16392     -1
16393   };
16394   int i;
16395
16396   for (i = 0; ids[i] > -1; i++)
16397     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16398 }
16399
16400 static void MapGameButtonsExt(boolean on_tape)
16401 {
16402   int i;
16403
16404   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16405   {
16406     if ((i == GAME_CTRL_ID_UNDO ||
16407          i == GAME_CTRL_ID_REDO) &&
16408         game_status != GAME_MODE_PLAYING)
16409       continue;
16410
16411     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16412       MapGadget(game_gadget[i]);
16413   }
16414
16415   UnmapGameButtonsAtSamePosition_All();
16416
16417   RedrawGameButtons();
16418 }
16419
16420 static void UnmapGameButtonsExt(boolean on_tape)
16421 {
16422   int i;
16423
16424   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16425     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16426       UnmapGadget(game_gadget[i]);
16427 }
16428
16429 static void RedrawGameButtonsExt(boolean on_tape)
16430 {
16431   int i;
16432
16433   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16434     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16435       RedrawGadget(game_gadget[i]);
16436 }
16437
16438 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16439 {
16440   if (gi == NULL)
16441     return;
16442
16443   gi->checked = state;
16444 }
16445
16446 static void RedrawSoundButtonGadget(int id)
16447 {
16448   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16449              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16450              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16451              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16452              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16453              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16454              id);
16455
16456   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16457   RedrawGadget(game_gadget[id2]);
16458 }
16459
16460 void MapGameButtons(void)
16461 {
16462   MapGameButtonsExt(FALSE);
16463 }
16464
16465 void UnmapGameButtons(void)
16466 {
16467   UnmapGameButtonsExt(FALSE);
16468 }
16469
16470 void RedrawGameButtons(void)
16471 {
16472   RedrawGameButtonsExt(FALSE);
16473 }
16474
16475 void MapGameButtonsOnTape(void)
16476 {
16477   MapGameButtonsExt(TRUE);
16478 }
16479
16480 void UnmapGameButtonsOnTape(void)
16481 {
16482   UnmapGameButtonsExt(TRUE);
16483 }
16484
16485 void RedrawGameButtonsOnTape(void)
16486 {
16487   RedrawGameButtonsExt(TRUE);
16488 }
16489
16490 static void GameUndoRedoExt(void)
16491 {
16492   ClearPlayerAction();
16493
16494   tape.pausing = TRUE;
16495
16496   RedrawPlayfield();
16497   UpdateAndDisplayGameControlValues();
16498
16499   DrawCompleteVideoDisplay();
16500   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16501   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16502   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16503
16504   ModifyPauseButtons();
16505
16506   BackToFront();
16507 }
16508
16509 static void GameUndo(int steps)
16510 {
16511   if (!CheckEngineSnapshotList())
16512     return;
16513
16514   int tape_property_bits = tape.property_bits;
16515
16516   LoadEngineSnapshot_Undo(steps);
16517
16518   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16519
16520   GameUndoRedoExt();
16521 }
16522
16523 static void GameRedo(int steps)
16524 {
16525   if (!CheckEngineSnapshotList())
16526     return;
16527
16528   int tape_property_bits = tape.property_bits;
16529
16530   LoadEngineSnapshot_Redo(steps);
16531
16532   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16533
16534   GameUndoRedoExt();
16535 }
16536
16537 static void HandleGameButtonsExt(int id, int button)
16538 {
16539   static boolean game_undo_executed = FALSE;
16540   int steps = BUTTON_STEPSIZE(button);
16541   boolean handle_game_buttons =
16542     (game_status == GAME_MODE_PLAYING ||
16543      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16544
16545   if (!handle_game_buttons)
16546     return;
16547
16548   switch (id)
16549   {
16550     case GAME_CTRL_ID_STOP:
16551     case GAME_CTRL_ID_PANEL_STOP:
16552     case GAME_CTRL_ID_TOUCH_STOP:
16553       TapeStopGame();
16554
16555       break;
16556
16557     case GAME_CTRL_ID_PAUSE:
16558     case GAME_CTRL_ID_PAUSE2:
16559     case GAME_CTRL_ID_PANEL_PAUSE:
16560     case GAME_CTRL_ID_TOUCH_PAUSE:
16561       if (network.enabled && game_status == GAME_MODE_PLAYING)
16562       {
16563         if (tape.pausing)
16564           SendToServer_ContinuePlaying();
16565         else
16566           SendToServer_PausePlaying();
16567       }
16568       else
16569         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16570
16571       game_undo_executed = FALSE;
16572
16573       break;
16574
16575     case GAME_CTRL_ID_PLAY:
16576     case GAME_CTRL_ID_PANEL_PLAY:
16577       if (game_status == GAME_MODE_MAIN)
16578       {
16579         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16580       }
16581       else if (tape.pausing)
16582       {
16583         if (network.enabled)
16584           SendToServer_ContinuePlaying();
16585         else
16586           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16587       }
16588       break;
16589
16590     case GAME_CTRL_ID_UNDO:
16591       // Important: When using "save snapshot when collecting an item" mode,
16592       // load last (current) snapshot for first "undo" after pressing "pause"
16593       // (else the last-but-one snapshot would be loaded, because the snapshot
16594       // pointer already points to the last snapshot when pressing "pause",
16595       // which is fine for "every step/move" mode, but not for "every collect")
16596       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16597           !game_undo_executed)
16598         steps--;
16599
16600       game_undo_executed = TRUE;
16601
16602       GameUndo(steps);
16603       break;
16604
16605     case GAME_CTRL_ID_REDO:
16606       GameRedo(steps);
16607       break;
16608
16609     case GAME_CTRL_ID_SAVE:
16610       TapeQuickSave();
16611       break;
16612
16613     case GAME_CTRL_ID_LOAD:
16614       TapeQuickLoad();
16615       break;
16616
16617     case SOUND_CTRL_ID_MUSIC:
16618     case SOUND_CTRL_ID_PANEL_MUSIC:
16619       if (setup.sound_music)
16620       { 
16621         setup.sound_music = FALSE;
16622
16623         FadeMusic();
16624       }
16625       else if (audio.music_available)
16626       { 
16627         setup.sound = setup.sound_music = TRUE;
16628
16629         SetAudioMode(setup.sound);
16630
16631         if (game_status == GAME_MODE_PLAYING)
16632           PlayLevelMusic();
16633       }
16634
16635       RedrawSoundButtonGadget(id);
16636
16637       break;
16638
16639     case SOUND_CTRL_ID_LOOPS:
16640     case SOUND_CTRL_ID_PANEL_LOOPS:
16641       if (setup.sound_loops)
16642         setup.sound_loops = FALSE;
16643       else if (audio.loops_available)
16644       {
16645         setup.sound = setup.sound_loops = TRUE;
16646
16647         SetAudioMode(setup.sound);
16648       }
16649
16650       RedrawSoundButtonGadget(id);
16651
16652       break;
16653
16654     case SOUND_CTRL_ID_SIMPLE:
16655     case SOUND_CTRL_ID_PANEL_SIMPLE:
16656       if (setup.sound_simple)
16657         setup.sound_simple = FALSE;
16658       else if (audio.sound_available)
16659       {
16660         setup.sound = setup.sound_simple = TRUE;
16661
16662         SetAudioMode(setup.sound);
16663       }
16664
16665       RedrawSoundButtonGadget(id);
16666
16667       break;
16668
16669     default:
16670       break;
16671   }
16672 }
16673
16674 static void HandleGameButtons(struct GadgetInfo *gi)
16675 {
16676   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16677 }
16678
16679 void HandleSoundButtonKeys(Key key)
16680 {
16681   if (key == setup.shortcut.sound_simple)
16682     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16683   else if (key == setup.shortcut.sound_loops)
16684     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16685   else if (key == setup.shortcut.sound_music)
16686     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16687 }