added setting global animation position from CE triggering that animation
[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   {
10720     int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
10721     int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
10722     int sx = FX + SCREENX(x) * TILEX_VAR;
10723     int sy = FY + SCREENY(y) * TILEY_VAR;
10724
10725     HandleGlobalAnimEventByElementChange(element, page, sx - fx, sy - fy);
10726   }
10727
10728   if (change->explode)
10729   {
10730     Bang(x, y);
10731
10732     return TRUE;
10733   }
10734
10735   if (change->use_target_content)
10736   {
10737     boolean complete_replace = TRUE;
10738     boolean can_replace[3][3];
10739     int xx, yy;
10740
10741     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10742     {
10743       boolean is_empty;
10744       boolean is_walkable;
10745       boolean is_diggable;
10746       boolean is_collectible;
10747       boolean is_removable;
10748       boolean is_destructible;
10749       int ex = x + xx - 1;
10750       int ey = y + yy - 1;
10751       int content_element = change->target_content.e[xx][yy];
10752       int e;
10753
10754       can_replace[xx][yy] = TRUE;
10755
10756       if (ex == x && ey == y)   // do not check changing element itself
10757         continue;
10758
10759       if (content_element == EL_EMPTY_SPACE)
10760       {
10761         can_replace[xx][yy] = FALSE;    // do not replace border with space
10762
10763         continue;
10764       }
10765
10766       if (!IN_LEV_FIELD(ex, ey))
10767       {
10768         can_replace[xx][yy] = FALSE;
10769         complete_replace = FALSE;
10770
10771         continue;
10772       }
10773
10774       e = Tile[ex][ey];
10775
10776       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10777         e = MovingOrBlocked2Element(ex, ey);
10778
10779       is_empty = (IS_FREE(ex, ey) ||
10780                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10781
10782       is_walkable     = (is_empty || IS_WALKABLE(e));
10783       is_diggable     = (is_empty || IS_DIGGABLE(e));
10784       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10785       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10786       is_removable    = (is_diggable || is_collectible);
10787
10788       can_replace[xx][yy] =
10789         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10790           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10791           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10792           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10793           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10794           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10795          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10796
10797       if (!can_replace[xx][yy])
10798         complete_replace = FALSE;
10799     }
10800
10801     if (!change->only_if_complete || complete_replace)
10802     {
10803       boolean something_has_changed = FALSE;
10804
10805       if (change->only_if_complete && change->use_random_replace &&
10806           RND(100) < change->random_percentage)
10807         return FALSE;
10808
10809       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10810       {
10811         int ex = x + xx - 1;
10812         int ey = y + yy - 1;
10813         int content_element;
10814
10815         if (can_replace[xx][yy] && (!change->use_random_replace ||
10816                                     RND(100) < change->random_percentage))
10817         {
10818           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10819             RemoveMovingField(ex, ey);
10820
10821           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10822
10823           content_element = change->target_content.e[xx][yy];
10824           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10825                                               ce_value, ce_score);
10826
10827           CreateElementFromChange(ex, ey, target_element);
10828
10829           something_has_changed = TRUE;
10830
10831           // for symmetry reasons, freeze newly created border elements
10832           if (ex != x || ey != y)
10833             Stop[ex][ey] = TRUE;        // no more moving in this frame
10834         }
10835       }
10836
10837       if (something_has_changed)
10838       {
10839         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10840         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10841       }
10842     }
10843   }
10844   else
10845   {
10846     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10847                                         ce_value, ce_score);
10848
10849     if (element == EL_DIAGONAL_GROWING ||
10850         element == EL_DIAGONAL_SHRINKING)
10851     {
10852       target_element = Store[x][y];
10853
10854       Store[x][y] = EL_EMPTY;
10855     }
10856
10857     // special case: element changes to player (and may be kept if walkable)
10858     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
10859       CreateElementFromChange(x, y, EL_EMPTY);
10860
10861     CreateElementFromChange(x, y, target_element);
10862
10863     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10864     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10865   }
10866
10867   // this uses direct change before indirect change
10868   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10869
10870   return TRUE;
10871 }
10872
10873 static void HandleElementChange(int x, int y, int page)
10874 {
10875   int element = MovingOrBlocked2Element(x, y);
10876   struct ElementInfo *ei = &element_info[element];
10877   struct ElementChangeInfo *change = &ei->change_page[page];
10878   boolean handle_action_before_change = FALSE;
10879
10880 #ifdef DEBUG
10881   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10882       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10883   {
10884     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10885           x, y, element, element_info[element].token_name);
10886     Debug("game:playing:HandleElementChange", "This should never happen!");
10887   }
10888 #endif
10889
10890   // this can happen with classic bombs on walkable, changing elements
10891   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10892   {
10893     return;
10894   }
10895
10896   if (ChangeDelay[x][y] == 0)           // initialize element change
10897   {
10898     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10899
10900     if (change->can_change)
10901     {
10902       // !!! not clear why graphic animation should be reset at all here !!!
10903       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10904       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10905
10906       /*
10907         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10908
10909         When using an animation frame delay of 1 (this only happens with
10910         "sp_zonk.moving.left/right" in the classic graphics), the default
10911         (non-moving) animation shows wrong animation frames (while the
10912         moving animation, like "sp_zonk.moving.left/right", is correct,
10913         so this graphical bug never shows up with the classic graphics).
10914         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10915         be drawn instead of the correct frames 0,1,2,3. This is caused by
10916         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10917         an element change: First when the change delay ("ChangeDelay[][]")
10918         counter has reached zero after decrementing, then a second time in
10919         the next frame (after "GfxFrame[][]" was already incremented) when
10920         "ChangeDelay[][]" is reset to the initial delay value again.
10921
10922         This causes frame 0 to be drawn twice, while the last frame won't
10923         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10924
10925         As some animations may already be cleverly designed around this bug
10926         (at least the "Snake Bite" snake tail animation does this), it cannot
10927         simply be fixed here without breaking such existing animations.
10928         Unfortunately, it cannot easily be detected if a graphics set was
10929         designed "before" or "after" the bug was fixed. As a workaround,
10930         a new graphics set option "game.graphics_engine_version" was added
10931         to be able to specify the game's major release version for which the
10932         graphics set was designed, which can then be used to decide if the
10933         bugfix should be used (version 4 and above) or not (version 3 or
10934         below, or if no version was specified at all, as with old sets).
10935
10936         (The wrong/fixed animation frames can be tested with the test level set
10937         "test_gfxframe" and level "000", which contains a specially prepared
10938         custom element at level position (x/y) == (11/9) which uses the zonk
10939         animation mentioned above. Using "game.graphics_engine_version: 4"
10940         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10941         This can also be seen from the debug output for this test element.)
10942       */
10943
10944       // when a custom element is about to change (for example by change delay),
10945       // do not reset graphic animation when the custom element is moving
10946       if (game.graphics_engine_version < 4 &&
10947           !IS_MOVING(x, y))
10948       {
10949         ResetGfxAnimation(x, y);
10950         ResetRandomAnimationValue(x, y);
10951       }
10952
10953       if (change->pre_change_function)
10954         change->pre_change_function(x, y);
10955     }
10956   }
10957
10958   ChangeDelay[x][y]--;
10959
10960   if (ChangeDelay[x][y] != 0)           // continue element change
10961   {
10962     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10963
10964     // also needed if CE can not change, but has CE delay with CE action
10965     if (IS_ANIMATED(graphic))
10966       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10967
10968     if (change->can_change)
10969     {
10970       if (change->change_function)
10971         change->change_function(x, y);
10972     }
10973   }
10974   else                                  // finish element change
10975   {
10976     if (ChangePage[x][y] != -1)         // remember page from delayed change
10977     {
10978       page = ChangePage[x][y];
10979       ChangePage[x][y] = -1;
10980
10981       change = &ei->change_page[page];
10982     }
10983
10984     if (IS_MOVING(x, y))                // never change a running system ;-)
10985     {
10986       ChangeDelay[x][y] = 1;            // try change after next move step
10987       ChangePage[x][y] = page;          // remember page to use for change
10988
10989       return;
10990     }
10991
10992     // special case: set new level random seed before changing element
10993     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10994       handle_action_before_change = TRUE;
10995
10996     if (change->has_action && handle_action_before_change)
10997       ExecuteCustomElementAction(x, y, element, page);
10998
10999     if (change->can_change)
11000     {
11001       if (ChangeElement(x, y, element, page))
11002       {
11003         if (change->post_change_function)
11004           change->post_change_function(x, y);
11005       }
11006     }
11007
11008     if (change->has_action && !handle_action_before_change)
11009       ExecuteCustomElementAction(x, y, element, page);
11010   }
11011 }
11012
11013 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11014                                               int trigger_element,
11015                                               int trigger_event,
11016                                               int trigger_player,
11017                                               int trigger_side,
11018                                               int trigger_page)
11019 {
11020   boolean change_done_any = FALSE;
11021   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11022   int i;
11023
11024   if (!(trigger_events[trigger_element][trigger_event]))
11025     return FALSE;
11026
11027   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11028
11029   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11030   {
11031     int element = EL_CUSTOM_START + i;
11032     boolean change_done = FALSE;
11033     int p;
11034
11035     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11036         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11037       continue;
11038
11039     for (p = 0; p < element_info[element].num_change_pages; p++)
11040     {
11041       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11042
11043       if (change->can_change_or_has_action &&
11044           change->has_event[trigger_event] &&
11045           change->trigger_side & trigger_side &&
11046           change->trigger_player & trigger_player &&
11047           change->trigger_page & trigger_page_bits &&
11048           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11049       {
11050         change->actual_trigger_element = trigger_element;
11051         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11052         change->actual_trigger_player_bits = trigger_player;
11053         change->actual_trigger_side = trigger_side;
11054         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11055         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11056
11057         if ((change->can_change && !change_done) || change->has_action)
11058         {
11059           int x, y;
11060
11061           SCAN_PLAYFIELD(x, y)
11062           {
11063             if (Tile[x][y] == element)
11064             {
11065               if (change->can_change && !change_done)
11066               {
11067                 // if element already changed in this frame, not only prevent
11068                 // another element change (checked in ChangeElement()), but
11069                 // also prevent additional element actions for this element
11070
11071                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11072                     !level.use_action_after_change_bug)
11073                   continue;
11074
11075                 ChangeDelay[x][y] = 1;
11076                 ChangeEvent[x][y] = trigger_event;
11077
11078                 HandleElementChange(x, y, p);
11079               }
11080               else if (change->has_action)
11081               {
11082                 // if element already changed in this frame, not only prevent
11083                 // another element change (checked in ChangeElement()), but
11084                 // also prevent additional element actions for this element
11085
11086                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11087                     !level.use_action_after_change_bug)
11088                   continue;
11089
11090                 ExecuteCustomElementAction(x, y, element, p);
11091                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11092               }
11093             }
11094           }
11095
11096           if (change->can_change)
11097           {
11098             change_done = TRUE;
11099             change_done_any = TRUE;
11100           }
11101         }
11102       }
11103     }
11104   }
11105
11106   RECURSION_LOOP_DETECTION_END();
11107
11108   return change_done_any;
11109 }
11110
11111 static boolean CheckElementChangeExt(int x, int y,
11112                                      int element,
11113                                      int trigger_element,
11114                                      int trigger_event,
11115                                      int trigger_player,
11116                                      int trigger_side)
11117 {
11118   boolean change_done = FALSE;
11119   int p;
11120
11121   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11122       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11123     return FALSE;
11124
11125   if (Tile[x][y] == EL_BLOCKED)
11126   {
11127     Blocked2Moving(x, y, &x, &y);
11128     element = Tile[x][y];
11129   }
11130
11131   // check if element has already changed or is about to change after moving
11132   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11133        Tile[x][y] != element) ||
11134
11135       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11136        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11137         ChangePage[x][y] != -1)))
11138     return FALSE;
11139
11140   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11141
11142   for (p = 0; p < element_info[element].num_change_pages; p++)
11143   {
11144     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11145
11146     /* check trigger element for all events where the element that is checked
11147        for changing interacts with a directly adjacent element -- this is
11148        different to element changes that affect other elements to change on the
11149        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11150     boolean check_trigger_element =
11151       (trigger_event == CE_NEXT_TO_X ||
11152        trigger_event == CE_TOUCHING_X ||
11153        trigger_event == CE_HITTING_X ||
11154        trigger_event == CE_HIT_BY_X ||
11155        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11156
11157     if (change->can_change_or_has_action &&
11158         change->has_event[trigger_event] &&
11159         change->trigger_side & trigger_side &&
11160         change->trigger_player & trigger_player &&
11161         (!check_trigger_element ||
11162          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11163     {
11164       change->actual_trigger_element = trigger_element;
11165       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11166       change->actual_trigger_player_bits = trigger_player;
11167       change->actual_trigger_side = trigger_side;
11168       change->actual_trigger_ce_value = CustomValue[x][y];
11169       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11170
11171       // special case: trigger element not at (x,y) position for some events
11172       if (check_trigger_element)
11173       {
11174         static struct
11175         {
11176           int dx, dy;
11177         } move_xy[] =
11178           {
11179             {  0,  0 },
11180             { -1,  0 },
11181             { +1,  0 },
11182             {  0,  0 },
11183             {  0, -1 },
11184             {  0,  0 }, { 0, 0 }, { 0, 0 },
11185             {  0, +1 }
11186           };
11187
11188         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11189         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11190
11191         change->actual_trigger_ce_value = CustomValue[xx][yy];
11192         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11193       }
11194
11195       if (change->can_change && !change_done)
11196       {
11197         ChangeDelay[x][y] = 1;
11198         ChangeEvent[x][y] = trigger_event;
11199
11200         HandleElementChange(x, y, p);
11201
11202         change_done = TRUE;
11203       }
11204       else if (change->has_action)
11205       {
11206         ExecuteCustomElementAction(x, y, element, p);
11207         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11208       }
11209     }
11210   }
11211
11212   RECURSION_LOOP_DETECTION_END();
11213
11214   return change_done;
11215 }
11216
11217 static void PlayPlayerSound(struct PlayerInfo *player)
11218 {
11219   int jx = player->jx, jy = player->jy;
11220   int sound_element = player->artwork_element;
11221   int last_action = player->last_action_waiting;
11222   int action = player->action_waiting;
11223
11224   if (player->is_waiting)
11225   {
11226     if (action != last_action)
11227       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11228     else
11229       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11230   }
11231   else
11232   {
11233     if (action != last_action)
11234       StopSound(element_info[sound_element].sound[last_action]);
11235
11236     if (last_action == ACTION_SLEEPING)
11237       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11238   }
11239 }
11240
11241 static void PlayAllPlayersSound(void)
11242 {
11243   int i;
11244
11245   for (i = 0; i < MAX_PLAYERS; i++)
11246     if (stored_player[i].active)
11247       PlayPlayerSound(&stored_player[i]);
11248 }
11249
11250 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11251 {
11252   boolean last_waiting = player->is_waiting;
11253   int move_dir = player->MovDir;
11254
11255   player->dir_waiting = move_dir;
11256   player->last_action_waiting = player->action_waiting;
11257
11258   if (is_waiting)
11259   {
11260     if (!last_waiting)          // not waiting -> waiting
11261     {
11262       player->is_waiting = TRUE;
11263
11264       player->frame_counter_bored =
11265         FrameCounter +
11266         game.player_boring_delay_fixed +
11267         GetSimpleRandom(game.player_boring_delay_random);
11268       player->frame_counter_sleeping =
11269         FrameCounter +
11270         game.player_sleeping_delay_fixed +
11271         GetSimpleRandom(game.player_sleeping_delay_random);
11272
11273       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11274     }
11275
11276     if (game.player_sleeping_delay_fixed +
11277         game.player_sleeping_delay_random > 0 &&
11278         player->anim_delay_counter == 0 &&
11279         player->post_delay_counter == 0 &&
11280         FrameCounter >= player->frame_counter_sleeping)
11281       player->is_sleeping = TRUE;
11282     else if (game.player_boring_delay_fixed +
11283              game.player_boring_delay_random > 0 &&
11284              FrameCounter >= player->frame_counter_bored)
11285       player->is_bored = TRUE;
11286
11287     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11288                               player->is_bored ? ACTION_BORING :
11289                               ACTION_WAITING);
11290
11291     if (player->is_sleeping && player->use_murphy)
11292     {
11293       // special case for sleeping Murphy when leaning against non-free tile
11294
11295       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11296           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11297            !IS_MOVING(player->jx - 1, player->jy)))
11298         move_dir = MV_LEFT;
11299       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11300                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11301                 !IS_MOVING(player->jx + 1, player->jy)))
11302         move_dir = MV_RIGHT;
11303       else
11304         player->is_sleeping = FALSE;
11305
11306       player->dir_waiting = move_dir;
11307     }
11308
11309     if (player->is_sleeping)
11310     {
11311       if (player->num_special_action_sleeping > 0)
11312       {
11313         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11314         {
11315           int last_special_action = player->special_action_sleeping;
11316           int num_special_action = player->num_special_action_sleeping;
11317           int special_action =
11318             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11319              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11320              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11321              last_special_action + 1 : ACTION_SLEEPING);
11322           int special_graphic =
11323             el_act_dir2img(player->artwork_element, special_action, move_dir);
11324
11325           player->anim_delay_counter =
11326             graphic_info[special_graphic].anim_delay_fixed +
11327             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11328           player->post_delay_counter =
11329             graphic_info[special_graphic].post_delay_fixed +
11330             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11331
11332           player->special_action_sleeping = special_action;
11333         }
11334
11335         if (player->anim_delay_counter > 0)
11336         {
11337           player->action_waiting = player->special_action_sleeping;
11338           player->anim_delay_counter--;
11339         }
11340         else if (player->post_delay_counter > 0)
11341         {
11342           player->post_delay_counter--;
11343         }
11344       }
11345     }
11346     else if (player->is_bored)
11347     {
11348       if (player->num_special_action_bored > 0)
11349       {
11350         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11351         {
11352           int special_action =
11353             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11354           int special_graphic =
11355             el_act_dir2img(player->artwork_element, special_action, move_dir);
11356
11357           player->anim_delay_counter =
11358             graphic_info[special_graphic].anim_delay_fixed +
11359             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11360           player->post_delay_counter =
11361             graphic_info[special_graphic].post_delay_fixed +
11362             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11363
11364           player->special_action_bored = special_action;
11365         }
11366
11367         if (player->anim_delay_counter > 0)
11368         {
11369           player->action_waiting = player->special_action_bored;
11370           player->anim_delay_counter--;
11371         }
11372         else if (player->post_delay_counter > 0)
11373         {
11374           player->post_delay_counter--;
11375         }
11376       }
11377     }
11378   }
11379   else if (last_waiting)        // waiting -> not waiting
11380   {
11381     player->is_waiting = FALSE;
11382     player->is_bored = FALSE;
11383     player->is_sleeping = FALSE;
11384
11385     player->frame_counter_bored = -1;
11386     player->frame_counter_sleeping = -1;
11387
11388     player->anim_delay_counter = 0;
11389     player->post_delay_counter = 0;
11390
11391     player->dir_waiting = player->MovDir;
11392     player->action_waiting = ACTION_DEFAULT;
11393
11394     player->special_action_bored = ACTION_DEFAULT;
11395     player->special_action_sleeping = ACTION_DEFAULT;
11396   }
11397 }
11398
11399 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11400 {
11401   if ((!player->is_moving  && player->was_moving) ||
11402       (player->MovPos == 0 && player->was_moving) ||
11403       (player->is_snapping && !player->was_snapping) ||
11404       (player->is_dropping && !player->was_dropping))
11405   {
11406     if (!CheckSaveEngineSnapshotToList())
11407       return;
11408
11409     player->was_moving = FALSE;
11410     player->was_snapping = TRUE;
11411     player->was_dropping = TRUE;
11412   }
11413   else
11414   {
11415     if (player->is_moving)
11416       player->was_moving = TRUE;
11417
11418     if (!player->is_snapping)
11419       player->was_snapping = FALSE;
11420
11421     if (!player->is_dropping)
11422       player->was_dropping = FALSE;
11423   }
11424
11425   static struct MouseActionInfo mouse_action_last = { 0 };
11426   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11427   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11428
11429   if (new_released)
11430     CheckSaveEngineSnapshotToList();
11431
11432   mouse_action_last = mouse_action;
11433 }
11434
11435 static void CheckSingleStepMode(struct PlayerInfo *player)
11436 {
11437   if (tape.single_step && tape.recording && !tape.pausing)
11438   {
11439     // as it is called "single step mode", just return to pause mode when the
11440     // player stopped moving after one tile (or never starts moving at all)
11441     // (reverse logic needed here in case single step mode used in team mode)
11442     if (player->is_moving ||
11443         player->is_pushing ||
11444         player->is_dropping_pressed ||
11445         player->effective_mouse_action.button)
11446       game.enter_single_step_mode = FALSE;
11447   }
11448
11449   CheckSaveEngineSnapshot(player);
11450 }
11451
11452 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11453 {
11454   int left      = player_action & JOY_LEFT;
11455   int right     = player_action & JOY_RIGHT;
11456   int up        = player_action & JOY_UP;
11457   int down      = player_action & JOY_DOWN;
11458   int button1   = player_action & JOY_BUTTON_1;
11459   int button2   = player_action & JOY_BUTTON_2;
11460   int dx        = (left ? -1 : right ? 1 : 0);
11461   int dy        = (up   ? -1 : down  ? 1 : 0);
11462
11463   if (!player->active || tape.pausing)
11464     return 0;
11465
11466   if (player_action)
11467   {
11468     if (button1)
11469       SnapField(player, dx, dy);
11470     else
11471     {
11472       if (button2)
11473         DropElement(player);
11474
11475       MovePlayer(player, dx, dy);
11476     }
11477
11478     CheckSingleStepMode(player);
11479
11480     SetPlayerWaiting(player, FALSE);
11481
11482     return player_action;
11483   }
11484   else
11485   {
11486     // no actions for this player (no input at player's configured device)
11487
11488     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11489     SnapField(player, 0, 0);
11490     CheckGravityMovementWhenNotMoving(player);
11491
11492     if (player->MovPos == 0)
11493       SetPlayerWaiting(player, TRUE);
11494
11495     if (player->MovPos == 0)    // needed for tape.playing
11496       player->is_moving = FALSE;
11497
11498     player->is_dropping = FALSE;
11499     player->is_dropping_pressed = FALSE;
11500     player->drop_pressed_delay = 0;
11501
11502     CheckSingleStepMode(player);
11503
11504     return 0;
11505   }
11506 }
11507
11508 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11509                                          byte *tape_action)
11510 {
11511   if (!tape.use_mouse_actions)
11512     return;
11513
11514   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11515   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11516   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11517 }
11518
11519 static void SetTapeActionFromMouseAction(byte *tape_action,
11520                                          struct MouseActionInfo *mouse_action)
11521 {
11522   if (!tape.use_mouse_actions)
11523     return;
11524
11525   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11526   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11527   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11528 }
11529
11530 static void CheckLevelSolved(void)
11531 {
11532   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11533   {
11534     if (game_em.level_solved &&
11535         !game_em.game_over)                             // game won
11536     {
11537       LevelSolved();
11538
11539       game_em.game_over = TRUE;
11540
11541       game.all_players_gone = TRUE;
11542     }
11543
11544     if (game_em.game_over)                              // game lost
11545       game.all_players_gone = TRUE;
11546   }
11547   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11548   {
11549     if (game_sp.level_solved &&
11550         !game_sp.game_over)                             // game won
11551     {
11552       LevelSolved();
11553
11554       game_sp.game_over = TRUE;
11555
11556       game.all_players_gone = TRUE;
11557     }
11558
11559     if (game_sp.game_over)                              // game lost
11560       game.all_players_gone = TRUE;
11561   }
11562   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11563   {
11564     if (game_mm.level_solved &&
11565         !game_mm.game_over)                             // game won
11566     {
11567       LevelSolved();
11568
11569       game_mm.game_over = TRUE;
11570
11571       game.all_players_gone = TRUE;
11572     }
11573
11574     if (game_mm.game_over)                              // game lost
11575       game.all_players_gone = TRUE;
11576   }
11577 }
11578
11579 static void CheckLevelTime_StepCounter(void)
11580 {
11581   int i;
11582
11583   TimePlayed++;
11584
11585   if (TimeLeft > 0)
11586   {
11587     TimeLeft--;
11588
11589     if (TimeLeft <= 10 && game.time_limit && !game.LevelSolved)
11590       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11591
11592     game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11593
11594     DisplayGameControlValues();
11595
11596     if (!TimeLeft && game.time_limit && !game.LevelSolved)
11597       for (i = 0; i < MAX_PLAYERS; i++)
11598         KillPlayer(&stored_player[i]);
11599   }
11600   else if (game.no_level_time_limit && !game.all_players_gone)
11601   {
11602     game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11603
11604     DisplayGameControlValues();
11605   }
11606 }
11607
11608 static void CheckLevelTime(void)
11609 {
11610   int i;
11611
11612   if (TimeFrames >= FRAMES_PER_SECOND)
11613   {
11614     TimeFrames = 0;
11615     TapeTime++;
11616
11617     for (i = 0; i < MAX_PLAYERS; i++)
11618     {
11619       struct PlayerInfo *player = &stored_player[i];
11620
11621       if (SHIELD_ON(player))
11622       {
11623         player->shield_normal_time_left--;
11624
11625         if (player->shield_deadly_time_left > 0)
11626           player->shield_deadly_time_left--;
11627       }
11628     }
11629
11630     if (!game.LevelSolved && !level.use_step_counter)
11631     {
11632       TimePlayed++;
11633
11634       if (TimeLeft > 0)
11635       {
11636         TimeLeft--;
11637
11638         if (TimeLeft <= 10 && game.time_limit)
11639           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11640
11641         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11642            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11643
11644         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11645
11646         if (!TimeLeft && game.time_limit)
11647         {
11648           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11649             game_em.lev->killed_out_of_time = TRUE;
11650           else
11651             for (i = 0; i < MAX_PLAYERS; i++)
11652               KillPlayer(&stored_player[i]);
11653         }
11654       }
11655       else if (game.no_level_time_limit && !game.all_players_gone)
11656       {
11657         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11658       }
11659
11660       game_em.lev->time = (game.no_level_time_limit ? TimePlayed : TimeLeft);
11661     }
11662
11663     if (tape.recording || tape.playing)
11664       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11665   }
11666
11667   if (tape.recording || tape.playing)
11668     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11669
11670   UpdateAndDisplayGameControlValues();
11671 }
11672
11673 void AdvanceFrameAndPlayerCounters(int player_nr)
11674 {
11675   int i;
11676
11677   // advance frame counters (global frame counter and time frame counter)
11678   FrameCounter++;
11679   TimeFrames++;
11680
11681   // advance player counters (counters for move delay, move animation etc.)
11682   for (i = 0; i < MAX_PLAYERS; i++)
11683   {
11684     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11685     int move_delay_value = stored_player[i].move_delay_value;
11686     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11687
11688     if (!advance_player_counters)       // not all players may be affected
11689       continue;
11690
11691     if (move_frames == 0)       // less than one move per game frame
11692     {
11693       int stepsize = TILEX / move_delay_value;
11694       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11695       int count = (stored_player[i].is_moving ?
11696                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11697
11698       if (count % delay == 0)
11699         move_frames = 1;
11700     }
11701
11702     stored_player[i].Frame += move_frames;
11703
11704     if (stored_player[i].MovPos != 0)
11705       stored_player[i].StepFrame += move_frames;
11706
11707     if (stored_player[i].move_delay > 0)
11708       stored_player[i].move_delay--;
11709
11710     // due to bugs in previous versions, counter must count up, not down
11711     if (stored_player[i].push_delay != -1)
11712       stored_player[i].push_delay++;
11713
11714     if (stored_player[i].drop_delay > 0)
11715       stored_player[i].drop_delay--;
11716
11717     if (stored_player[i].is_dropping_pressed)
11718       stored_player[i].drop_pressed_delay++;
11719   }
11720 }
11721
11722 void AdvanceFrameCounter(void)
11723 {
11724   FrameCounter++;
11725 }
11726
11727 void AdvanceGfxFrame(void)
11728 {
11729   int x, y;
11730
11731   SCAN_PLAYFIELD(x, y)
11732   {
11733     GfxFrame[x][y]++;
11734   }
11735 }
11736
11737 static void HandleMouseAction(struct MouseActionInfo *mouse_action,
11738                               struct MouseActionInfo *mouse_action_last)
11739 {
11740   if (mouse_action->button)
11741   {
11742     int new_button = (mouse_action->button && mouse_action_last->button == 0);
11743     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action->button);
11744     int x = mouse_action->lx;
11745     int y = mouse_action->ly;
11746     int element = Tile[x][y];
11747
11748     if (new_button)
11749     {
11750       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
11751       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
11752                                          ch_button);
11753     }
11754
11755     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
11756     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
11757                                        ch_button);
11758
11759     if (level.use_step_counter)
11760     {
11761       boolean counted_click = FALSE;
11762
11763       // element clicked that can change when clicked/pressed
11764       if (CAN_CHANGE_OR_HAS_ACTION(element) &&
11765           (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
11766            HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE)))
11767         counted_click = TRUE;
11768
11769       // element clicked that can trigger change when clicked/pressed
11770       if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] ||
11771           trigger_events[element][CE_MOUSE_PRESSED_ON_X])
11772         counted_click = TRUE;
11773
11774       if (new_button && counted_click)
11775         CheckLevelTime_StepCounter();
11776     }
11777   }
11778 }
11779
11780 void StartGameActions(boolean init_network_game, boolean record_tape,
11781                       int random_seed)
11782 {
11783   unsigned int new_random_seed = InitRND(random_seed);
11784
11785   if (record_tape)
11786     TapeStartRecording(new_random_seed);
11787
11788   if (setup.auto_pause_on_start && !tape.pausing)
11789     TapeTogglePause(TAPE_TOGGLE_MANUAL);
11790
11791   if (init_network_game)
11792   {
11793     SendToServer_LevelFile();
11794     SendToServer_StartPlaying();
11795
11796     return;
11797   }
11798
11799   InitGame();
11800 }
11801
11802 static void GameActionsExt(void)
11803 {
11804 #if 0
11805   static unsigned int game_frame_delay = 0;
11806 #endif
11807   unsigned int game_frame_delay_value;
11808   byte *recorded_player_action;
11809   byte summarized_player_action = 0;
11810   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11811   int i;
11812
11813   // detect endless loops, caused by custom element programming
11814   if (recursion_loop_detected && recursion_loop_depth == 0)
11815   {
11816     char *message = getStringCat3("Internal Error! Element ",
11817                                   EL_NAME(recursion_loop_element),
11818                                   " caused endless loop! Quit the game?");
11819
11820     Warn("element '%s' caused endless loop in game engine",
11821          EL_NAME(recursion_loop_element));
11822
11823     RequestQuitGameExt(program.headless, level_editor_test_game, message);
11824
11825     recursion_loop_detected = FALSE;    // if game should be continued
11826
11827     free(message);
11828
11829     return;
11830   }
11831
11832   if (game.restart_level)
11833     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11834
11835   CheckLevelSolved();
11836
11837   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11838     GameWon();
11839
11840   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11841     TapeStop();
11842
11843   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11844     return;
11845
11846   game_frame_delay_value =
11847     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11848
11849   if (tape.playing && tape.warp_forward && !tape.pausing)
11850     game_frame_delay_value = 0;
11851
11852   SetVideoFrameDelay(game_frame_delay_value);
11853
11854   // (de)activate virtual buttons depending on current game status
11855   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11856   {
11857     if (game.all_players_gone)  // if no players there to be controlled anymore
11858       SetOverlayActive(FALSE);
11859     else if (!tape.playing)     // if game continues after tape stopped playing
11860       SetOverlayActive(TRUE);
11861   }
11862
11863 #if 0
11864 #if 0
11865   // ---------- main game synchronization point ----------
11866
11867   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11868
11869   Debug("game:playing:skip", "skip == %d", skip);
11870
11871 #else
11872   // ---------- main game synchronization point ----------
11873
11874   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11875 #endif
11876 #endif
11877
11878   if (network_playing && !network_player_action_received)
11879   {
11880     // try to get network player actions in time
11881
11882     // last chance to get network player actions without main loop delay
11883     HandleNetworking();
11884
11885     // game was quit by network peer
11886     if (game_status != GAME_MODE_PLAYING)
11887       return;
11888
11889     // check if network player actions still missing and game still running
11890     if (!network_player_action_received && !checkGameEnded())
11891       return;           // failed to get network player actions in time
11892
11893     // do not yet reset "network_player_action_received" (for tape.pausing)
11894   }
11895
11896   if (tape.pausing)
11897     return;
11898
11899   // at this point we know that we really continue executing the game
11900
11901   network_player_action_received = FALSE;
11902
11903   // when playing tape, read previously recorded player input from tape data
11904   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11905
11906   local_player->effective_mouse_action = local_player->mouse_action;
11907
11908   if (recorded_player_action != NULL)
11909     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11910                                  recorded_player_action);
11911
11912   // TapePlayAction() may return NULL when toggling to "pause before death"
11913   if (tape.pausing)
11914     return;
11915
11916   if (tape.set_centered_player)
11917   {
11918     game.centered_player_nr_next = tape.centered_player_nr_next;
11919     game.set_centered_player = TRUE;
11920   }
11921
11922   for (i = 0; i < MAX_PLAYERS; i++)
11923   {
11924     summarized_player_action |= stored_player[i].action;
11925
11926     if (!network_playing && (game.team_mode || tape.playing))
11927       stored_player[i].effective_action = stored_player[i].action;
11928   }
11929
11930   if (network_playing && !checkGameEnded())
11931     SendToServer_MovePlayer(summarized_player_action);
11932
11933   // summarize all actions at local players mapped input device position
11934   // (this allows using different input devices in single player mode)
11935   if (!network.enabled && !game.team_mode)
11936     stored_player[map_player_action[local_player->index_nr]].effective_action =
11937       summarized_player_action;
11938
11939   // summarize all actions at centered player in local team mode
11940   if (tape.recording &&
11941       setup.team_mode && !network.enabled &&
11942       setup.input_on_focus &&
11943       game.centered_player_nr != -1)
11944   {
11945     for (i = 0; i < MAX_PLAYERS; i++)
11946       stored_player[map_player_action[i]].effective_action =
11947         (i == game.centered_player_nr ? summarized_player_action : 0);
11948   }
11949
11950   if (recorded_player_action != NULL)
11951     for (i = 0; i < MAX_PLAYERS; i++)
11952       stored_player[i].effective_action = recorded_player_action[i];
11953
11954   for (i = 0; i < MAX_PLAYERS; i++)
11955   {
11956     tape_action[i] = stored_player[i].effective_action;
11957
11958     /* (this may happen in the RND game engine if a player was not present on
11959        the playfield on level start, but appeared later from a custom element */
11960     if (setup.team_mode &&
11961         tape.recording &&
11962         tape_action[i] &&
11963         !tape.player_participates[i])
11964       tape.player_participates[i] = TRUE;
11965   }
11966
11967   SetTapeActionFromMouseAction(tape_action,
11968                                &local_player->effective_mouse_action);
11969
11970   // only record actions from input devices, but not programmed actions
11971   if (tape.recording)
11972     TapeRecordAction(tape_action);
11973
11974   // remember if game was played (especially after tape stopped playing)
11975   if (!tape.playing && summarized_player_action && !checkGameFailed())
11976     game.GamePlayed = TRUE;
11977
11978 #if USE_NEW_PLAYER_ASSIGNMENTS
11979   // !!! also map player actions in single player mode !!!
11980   // if (game.team_mode)
11981   if (1)
11982   {
11983     byte mapped_action[MAX_PLAYERS];
11984
11985 #if DEBUG_PLAYER_ACTIONS
11986     for (i = 0; i < MAX_PLAYERS; i++)
11987       DebugContinued("", "%d, ", stored_player[i].effective_action);
11988 #endif
11989
11990     for (i = 0; i < MAX_PLAYERS; i++)
11991       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11992
11993     for (i = 0; i < MAX_PLAYERS; i++)
11994       stored_player[i].effective_action = mapped_action[i];
11995
11996 #if DEBUG_PLAYER_ACTIONS
11997     DebugContinued("", "=> ");
11998     for (i = 0; i < MAX_PLAYERS; i++)
11999       DebugContinued("", "%d, ", stored_player[i].effective_action);
12000     DebugContinued("game:playing:player", "\n");
12001 #endif
12002   }
12003 #if DEBUG_PLAYER_ACTIONS
12004   else
12005   {
12006     for (i = 0; i < MAX_PLAYERS; i++)
12007       DebugContinued("", "%d, ", stored_player[i].effective_action);
12008     DebugContinued("game:playing:player", "\n");
12009   }
12010 #endif
12011 #endif
12012
12013   for (i = 0; i < MAX_PLAYERS; i++)
12014   {
12015     // allow engine snapshot in case of changed movement attempt
12016     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
12017         (stored_player[i].effective_action & KEY_MOTION))
12018       game.snapshot.changed_action = TRUE;
12019
12020     // allow engine snapshot in case of snapping/dropping attempt
12021     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
12022         (stored_player[i].effective_action & KEY_BUTTON) != 0)
12023       game.snapshot.changed_action = TRUE;
12024
12025     game.snapshot.last_action[i] = stored_player[i].effective_action;
12026   }
12027
12028   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12029   {
12030     GameActions_EM_Main();
12031   }
12032   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12033   {
12034     GameActions_SP_Main();
12035   }
12036   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
12037   {
12038     GameActions_MM_Main();
12039   }
12040   else
12041   {
12042     GameActions_RND_Main();
12043   }
12044
12045   BlitScreenToBitmap(backbuffer);
12046
12047   CheckLevelSolved();
12048   CheckLevelTime();
12049
12050   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
12051
12052   if (global.show_frames_per_second)
12053   {
12054     static unsigned int fps_counter = 0;
12055     static int fps_frames = 0;
12056     unsigned int fps_delay_ms = Counter() - fps_counter;
12057
12058     fps_frames++;
12059
12060     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
12061     {
12062       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12063
12064       fps_frames = 0;
12065       fps_counter = Counter();
12066
12067       // always draw FPS to screen after FPS value was updated
12068       redraw_mask |= REDRAW_FPS;
12069     }
12070
12071     // only draw FPS if no screen areas are deactivated (invisible warp mode)
12072     if (GetDrawDeactivationMask() == REDRAW_NONE)
12073       redraw_mask |= REDRAW_FPS;
12074   }
12075 }
12076
12077 static void GameActions_CheckSaveEngineSnapshot(void)
12078 {
12079   if (!game.snapshot.save_snapshot)
12080     return;
12081
12082   // clear flag for saving snapshot _before_ saving snapshot
12083   game.snapshot.save_snapshot = FALSE;
12084
12085   SaveEngineSnapshotToList();
12086 }
12087
12088 void GameActions(void)
12089 {
12090   GameActionsExt();
12091
12092   GameActions_CheckSaveEngineSnapshot();
12093 }
12094
12095 void GameActions_EM_Main(void)
12096 {
12097   byte effective_action[MAX_PLAYERS];
12098   int i;
12099
12100   for (i = 0; i < MAX_PLAYERS; i++)
12101     effective_action[i] = stored_player[i].effective_action;
12102
12103   GameActions_EM(effective_action);
12104 }
12105
12106 void GameActions_SP_Main(void)
12107 {
12108   byte effective_action[MAX_PLAYERS];
12109   int i;
12110
12111   for (i = 0; i < MAX_PLAYERS; i++)
12112     effective_action[i] = stored_player[i].effective_action;
12113
12114   GameActions_SP(effective_action);
12115
12116   for (i = 0; i < MAX_PLAYERS; i++)
12117   {
12118     if (stored_player[i].force_dropping)
12119       stored_player[i].action |= KEY_BUTTON_DROP;
12120
12121     stored_player[i].force_dropping = FALSE;
12122   }
12123 }
12124
12125 void GameActions_MM_Main(void)
12126 {
12127   AdvanceGfxFrame();
12128
12129   GameActions_MM(local_player->effective_mouse_action);
12130 }
12131
12132 void GameActions_RND_Main(void)
12133 {
12134   GameActions_RND();
12135 }
12136
12137 void GameActions_RND(void)
12138 {
12139   static struct MouseActionInfo mouse_action_last = { 0 };
12140   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12141   int magic_wall_x = 0, magic_wall_y = 0;
12142   int i, x, y, element, graphic, last_gfx_frame;
12143
12144   InitPlayfieldScanModeVars();
12145
12146   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12147   {
12148     SCAN_PLAYFIELD(x, y)
12149     {
12150       ChangeCount[x][y] = 0;
12151       ChangeEvent[x][y] = -1;
12152     }
12153   }
12154
12155   if (game.set_centered_player)
12156   {
12157     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12158
12159     // switching to "all players" only possible if all players fit to screen
12160     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12161     {
12162       game.centered_player_nr_next = game.centered_player_nr;
12163       game.set_centered_player = FALSE;
12164     }
12165
12166     // do not switch focus to non-existing (or non-active) player
12167     if (game.centered_player_nr_next >= 0 &&
12168         !stored_player[game.centered_player_nr_next].active)
12169     {
12170       game.centered_player_nr_next = game.centered_player_nr;
12171       game.set_centered_player = FALSE;
12172     }
12173   }
12174
12175   if (game.set_centered_player &&
12176       ScreenMovPos == 0)        // screen currently aligned at tile position
12177   {
12178     int sx, sy;
12179
12180     if (game.centered_player_nr_next == -1)
12181     {
12182       setScreenCenteredToAllPlayers(&sx, &sy);
12183     }
12184     else
12185     {
12186       sx = stored_player[game.centered_player_nr_next].jx;
12187       sy = stored_player[game.centered_player_nr_next].jy;
12188     }
12189
12190     game.centered_player_nr = game.centered_player_nr_next;
12191     game.set_centered_player = FALSE;
12192
12193     DrawRelocateScreen(0, 0, sx, sy, TRUE, setup.quick_switch);
12194     DrawGameDoorValues();
12195   }
12196
12197   // check single step mode (set flag and clear again if any player is active)
12198   game.enter_single_step_mode =
12199     (tape.single_step && tape.recording && !tape.pausing);
12200
12201   for (i = 0; i < MAX_PLAYERS; i++)
12202   {
12203     int actual_player_action = stored_player[i].effective_action;
12204
12205 #if 1
12206     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12207        - rnd_equinox_tetrachloride 048
12208        - rnd_equinox_tetrachloride_ii 096
12209        - rnd_emanuel_schmieg 002
12210        - doctor_sloan_ww 001, 020
12211     */
12212     if (stored_player[i].MovPos == 0)
12213       CheckGravityMovement(&stored_player[i]);
12214 #endif
12215
12216     // overwrite programmed action with tape action
12217     if (stored_player[i].programmed_action)
12218       actual_player_action = stored_player[i].programmed_action;
12219
12220     PlayerActions(&stored_player[i], actual_player_action);
12221
12222     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12223   }
12224
12225   // single step pause mode may already have been toggled by "ScrollPlayer()"
12226   if (game.enter_single_step_mode && !tape.pausing)
12227     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12228
12229   ScrollScreen(NULL, SCROLL_GO_ON);
12230
12231   /* for backwards compatibility, the following code emulates a fixed bug that
12232      occured when pushing elements (causing elements that just made their last
12233      pushing step to already (if possible) make their first falling step in the
12234      same game frame, which is bad); this code is also needed to use the famous
12235      "spring push bug" which is used in older levels and might be wanted to be
12236      used also in newer levels, but in this case the buggy pushing code is only
12237      affecting the "spring" element and no other elements */
12238
12239   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12240   {
12241     for (i = 0; i < MAX_PLAYERS; i++)
12242     {
12243       struct PlayerInfo *player = &stored_player[i];
12244       int x = player->jx;
12245       int y = player->jy;
12246
12247       if (player->active && player->is_pushing && player->is_moving &&
12248           IS_MOVING(x, y) &&
12249           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12250            Tile[x][y] == EL_SPRING))
12251       {
12252         ContinueMoving(x, y);
12253
12254         // continue moving after pushing (this is actually a bug)
12255         if (!IS_MOVING(x, y))
12256           Stop[x][y] = FALSE;
12257       }
12258     }
12259   }
12260
12261   SCAN_PLAYFIELD(x, y)
12262   {
12263     Last[x][y] = Tile[x][y];
12264
12265     ChangeCount[x][y] = 0;
12266     ChangeEvent[x][y] = -1;
12267
12268     // this must be handled before main playfield loop
12269     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12270     {
12271       MovDelay[x][y]--;
12272       if (MovDelay[x][y] <= 0)
12273         RemoveField(x, y);
12274     }
12275
12276     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12277     {
12278       MovDelay[x][y]--;
12279       if (MovDelay[x][y] <= 0)
12280       {
12281         int element = Store[x][y];
12282         int move_direction = MovDir[x][y];
12283         int player_index_bit = Store2[x][y];
12284
12285         Store[x][y] = 0;
12286         Store2[x][y] = 0;
12287
12288         RemoveField(x, y);
12289         TEST_DrawLevelField(x, y);
12290
12291         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12292
12293         if (IS_ENVELOPE(element))
12294           local_player->show_envelope = element;
12295       }
12296     }
12297
12298 #if DEBUG
12299     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12300     {
12301       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12302             x, y);
12303       Debug("game:playing:GameActions_RND", "This should never happen!");
12304
12305       ChangePage[x][y] = -1;
12306     }
12307 #endif
12308
12309     Stop[x][y] = FALSE;
12310     if (WasJustMoving[x][y] > 0)
12311       WasJustMoving[x][y]--;
12312     if (WasJustFalling[x][y] > 0)
12313       WasJustFalling[x][y]--;
12314     if (CheckCollision[x][y] > 0)
12315       CheckCollision[x][y]--;
12316     if (CheckImpact[x][y] > 0)
12317       CheckImpact[x][y]--;
12318
12319     GfxFrame[x][y]++;
12320
12321     /* reset finished pushing action (not done in ContinueMoving() to allow
12322        continuous pushing animation for elements with zero push delay) */
12323     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12324     {
12325       ResetGfxAnimation(x, y);
12326       TEST_DrawLevelField(x, y);
12327     }
12328
12329 #if DEBUG
12330     if (IS_BLOCKED(x, y))
12331     {
12332       int oldx, oldy;
12333
12334       Blocked2Moving(x, y, &oldx, &oldy);
12335       if (!IS_MOVING(oldx, oldy))
12336       {
12337         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12338         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12339         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12340         Debug("game:playing:GameActions_RND", "This should never happen!");
12341       }
12342     }
12343 #endif
12344   }
12345
12346   HandleMouseAction(&mouse_action, &mouse_action_last);
12347
12348   SCAN_PLAYFIELD(x, y)
12349   {
12350     element = Tile[x][y];
12351     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12352     last_gfx_frame = GfxFrame[x][y];
12353
12354     if (element == EL_EMPTY)
12355       graphic = el2img(GfxElementEmpty[x][y]);
12356
12357     ResetGfxFrame(x, y);
12358
12359     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12360       DrawLevelGraphicAnimation(x, y, graphic);
12361
12362     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12363         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12364       ResetRandomAnimationValue(x, y);
12365
12366     SetRandomAnimationValue(x, y);
12367
12368     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12369
12370     if (IS_INACTIVE(element))
12371     {
12372       if (IS_ANIMATED(graphic))
12373         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12374
12375       continue;
12376     }
12377
12378     // this may take place after moving, so 'element' may have changed
12379     if (IS_CHANGING(x, y) &&
12380         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12381     {
12382       int page = element_info[element].event_page_nr[CE_DELAY];
12383
12384       HandleElementChange(x, y, page);
12385
12386       element = Tile[x][y];
12387       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12388     }
12389
12390     CheckNextToConditions(x, y);
12391
12392     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12393     {
12394       StartMoving(x, y);
12395
12396       element = Tile[x][y];
12397       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12398
12399       if (IS_ANIMATED(graphic) &&
12400           !IS_MOVING(x, y) &&
12401           !Stop[x][y])
12402         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12403
12404       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12405         TEST_DrawTwinkleOnField(x, y);
12406     }
12407     else if (element == EL_ACID)
12408     {
12409       if (!Stop[x][y])
12410         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12411     }
12412     else if ((element == EL_EXIT_OPEN ||
12413               element == EL_EM_EXIT_OPEN ||
12414               element == EL_SP_EXIT_OPEN ||
12415               element == EL_STEEL_EXIT_OPEN ||
12416               element == EL_EM_STEEL_EXIT_OPEN ||
12417               element == EL_SP_TERMINAL ||
12418               element == EL_SP_TERMINAL_ACTIVE ||
12419               element == EL_EXTRA_TIME ||
12420               element == EL_SHIELD_NORMAL ||
12421               element == EL_SHIELD_DEADLY) &&
12422              IS_ANIMATED(graphic))
12423       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12424     else if (IS_MOVING(x, y))
12425       ContinueMoving(x, y);
12426     else if (IS_ACTIVE_BOMB(element))
12427       CheckDynamite(x, y);
12428     else if (element == EL_AMOEBA_GROWING)
12429       AmoebaGrowing(x, y);
12430     else if (element == EL_AMOEBA_SHRINKING)
12431       AmoebaShrinking(x, y);
12432
12433 #if !USE_NEW_AMOEBA_CODE
12434     else if (IS_AMOEBALIVE(element))
12435       AmoebaReproduce(x, y);
12436 #endif
12437
12438     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12439       Life(x, y);
12440     else if (element == EL_EXIT_CLOSED)
12441       CheckExit(x, y);
12442     else if (element == EL_EM_EXIT_CLOSED)
12443       CheckExitEM(x, y);
12444     else if (element == EL_STEEL_EXIT_CLOSED)
12445       CheckExitSteel(x, y);
12446     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12447       CheckExitSteelEM(x, y);
12448     else if (element == EL_SP_EXIT_CLOSED)
12449       CheckExitSP(x, y);
12450     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12451              element == EL_EXPANDABLE_STEELWALL_GROWING)
12452       WallGrowing(x, y);
12453     else if (element == EL_EXPANDABLE_WALL ||
12454              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12455              element == EL_EXPANDABLE_WALL_VERTICAL ||
12456              element == EL_EXPANDABLE_WALL_ANY ||
12457              element == EL_BD_EXPANDABLE_WALL ||
12458              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12459              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12460              element == EL_EXPANDABLE_STEELWALL_ANY)
12461       CheckWallGrowing(x, y);
12462     else if (element == EL_FLAMES)
12463       CheckForDragon(x, y);
12464     else if (element == EL_EXPLOSION)
12465       ; // drawing of correct explosion animation is handled separately
12466     else if (element == EL_ELEMENT_SNAPPING ||
12467              element == EL_DIAGONAL_SHRINKING ||
12468              element == EL_DIAGONAL_GROWING)
12469     {
12470       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y], GfxDir[x][y]);
12471
12472       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12473     }
12474     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12475       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12476
12477     if (IS_BELT_ACTIVE(element))
12478       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12479
12480     if (game.magic_wall_active)
12481     {
12482       int jx = local_player->jx, jy = local_player->jy;
12483
12484       // play the element sound at the position nearest to the player
12485       if ((element == EL_MAGIC_WALL_FULL ||
12486            element == EL_MAGIC_WALL_ACTIVE ||
12487            element == EL_MAGIC_WALL_EMPTYING ||
12488            element == EL_BD_MAGIC_WALL_FULL ||
12489            element == EL_BD_MAGIC_WALL_ACTIVE ||
12490            element == EL_BD_MAGIC_WALL_EMPTYING ||
12491            element == EL_DC_MAGIC_WALL_FULL ||
12492            element == EL_DC_MAGIC_WALL_ACTIVE ||
12493            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12494           ABS(x - jx) + ABS(y - jy) <
12495           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12496       {
12497         magic_wall_x = x;
12498         magic_wall_y = y;
12499       }
12500     }
12501   }
12502
12503 #if USE_NEW_AMOEBA_CODE
12504   // new experimental amoeba growth stuff
12505   if (!(FrameCounter % 8))
12506   {
12507     static unsigned int random = 1684108901;
12508
12509     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12510     {
12511       x = RND(lev_fieldx);
12512       y = RND(lev_fieldy);
12513       element = Tile[x][y];
12514
12515       if (!IS_PLAYER(x, y) &&
12516           (element == EL_EMPTY ||
12517            CAN_GROW_INTO(element) ||
12518            element == EL_QUICKSAND_EMPTY ||
12519            element == EL_QUICKSAND_FAST_EMPTY ||
12520            element == EL_ACID_SPLASH_LEFT ||
12521            element == EL_ACID_SPLASH_RIGHT))
12522       {
12523         if ((IN_LEV_FIELD(x, y - 1) && Tile[x][y - 1] == EL_AMOEBA_WET) ||
12524             (IN_LEV_FIELD(x - 1, y) && Tile[x - 1][y] == EL_AMOEBA_WET) ||
12525             (IN_LEV_FIELD(x + 1, y) && Tile[x + 1][y] == EL_AMOEBA_WET) ||
12526             (IN_LEV_FIELD(x, y + 1) && Tile[x][y + 1] == EL_AMOEBA_WET))
12527           Tile[x][y] = EL_AMOEBA_DROP;
12528       }
12529
12530       random = random * 129 + 1;
12531     }
12532   }
12533 #endif
12534
12535   game.explosions_delayed = FALSE;
12536
12537   SCAN_PLAYFIELD(x, y)
12538   {
12539     element = Tile[x][y];
12540
12541     if (ExplodeField[x][y])
12542       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12543     else if (element == EL_EXPLOSION)
12544       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12545
12546     ExplodeField[x][y] = EX_TYPE_NONE;
12547   }
12548
12549   game.explosions_delayed = TRUE;
12550
12551   if (game.magic_wall_active)
12552   {
12553     if (!(game.magic_wall_time_left % 4))
12554     {
12555       int element = Tile[magic_wall_x][magic_wall_y];
12556
12557       if (element == EL_BD_MAGIC_WALL_FULL ||
12558           element == EL_BD_MAGIC_WALL_ACTIVE ||
12559           element == EL_BD_MAGIC_WALL_EMPTYING)
12560         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12561       else if (element == EL_DC_MAGIC_WALL_FULL ||
12562                element == EL_DC_MAGIC_WALL_ACTIVE ||
12563                element == EL_DC_MAGIC_WALL_EMPTYING)
12564         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12565       else
12566         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12567     }
12568
12569     if (game.magic_wall_time_left > 0)
12570     {
12571       game.magic_wall_time_left--;
12572
12573       if (!game.magic_wall_time_left)
12574       {
12575         SCAN_PLAYFIELD(x, y)
12576         {
12577           element = Tile[x][y];
12578
12579           if (element == EL_MAGIC_WALL_ACTIVE ||
12580               element == EL_MAGIC_WALL_FULL)
12581           {
12582             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12583             TEST_DrawLevelField(x, y);
12584           }
12585           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12586                    element == EL_BD_MAGIC_WALL_FULL)
12587           {
12588             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12589             TEST_DrawLevelField(x, y);
12590           }
12591           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12592                    element == EL_DC_MAGIC_WALL_FULL)
12593           {
12594             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12595             TEST_DrawLevelField(x, y);
12596           }
12597         }
12598
12599         game.magic_wall_active = FALSE;
12600       }
12601     }
12602   }
12603
12604   if (game.light_time_left > 0)
12605   {
12606     game.light_time_left--;
12607
12608     if (game.light_time_left == 0)
12609       RedrawAllLightSwitchesAndInvisibleElements();
12610   }
12611
12612   if (game.timegate_time_left > 0)
12613   {
12614     game.timegate_time_left--;
12615
12616     if (game.timegate_time_left == 0)
12617       CloseAllOpenTimegates();
12618   }
12619
12620   if (game.lenses_time_left > 0)
12621   {
12622     game.lenses_time_left--;
12623
12624     if (game.lenses_time_left == 0)
12625       RedrawAllInvisibleElementsForLenses();
12626   }
12627
12628   if (game.magnify_time_left > 0)
12629   {
12630     game.magnify_time_left--;
12631
12632     if (game.magnify_time_left == 0)
12633       RedrawAllInvisibleElementsForMagnifier();
12634   }
12635
12636   for (i = 0; i < MAX_PLAYERS; i++)
12637   {
12638     struct PlayerInfo *player = &stored_player[i];
12639
12640     if (SHIELD_ON(player))
12641     {
12642       if (player->shield_deadly_time_left)
12643         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12644       else if (player->shield_normal_time_left)
12645         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12646     }
12647   }
12648
12649 #if USE_DELAYED_GFX_REDRAW
12650   SCAN_PLAYFIELD(x, y)
12651   {
12652     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12653     {
12654       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12655          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12656
12657       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12658         DrawLevelField(x, y);
12659
12660       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12661         DrawLevelFieldCrumbled(x, y);
12662
12663       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12664         DrawLevelFieldCrumbledNeighbours(x, y);
12665
12666       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12667         DrawTwinkleOnField(x, y);
12668     }
12669
12670     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12671   }
12672 #endif
12673
12674   DrawAllPlayers();
12675   PlayAllPlayersSound();
12676
12677   for (i = 0; i < MAX_PLAYERS; i++)
12678   {
12679     struct PlayerInfo *player = &stored_player[i];
12680
12681     if (player->show_envelope != 0 && (!player->active ||
12682                                        player->MovPos == 0))
12683     {
12684       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12685
12686       player->show_envelope = 0;
12687     }
12688   }
12689
12690   // use random number generator in every frame to make it less predictable
12691   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12692     RND(1);
12693
12694   mouse_action_last = mouse_action;
12695 }
12696
12697 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12698 {
12699   int min_x = x, min_y = y, max_x = x, max_y = y;
12700   int scr_fieldx = getScreenFieldSizeX();
12701   int scr_fieldy = getScreenFieldSizeY();
12702   int i;
12703
12704   for (i = 0; i < MAX_PLAYERS; i++)
12705   {
12706     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12707
12708     if (!stored_player[i].active || &stored_player[i] == player)
12709       continue;
12710
12711     min_x = MIN(min_x, jx);
12712     min_y = MIN(min_y, jy);
12713     max_x = MAX(max_x, jx);
12714     max_y = MAX(max_y, jy);
12715   }
12716
12717   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12718 }
12719
12720 static boolean AllPlayersInVisibleScreen(void)
12721 {
12722   int i;
12723
12724   for (i = 0; i < MAX_PLAYERS; i++)
12725   {
12726     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12727
12728     if (!stored_player[i].active)
12729       continue;
12730
12731     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12732       return FALSE;
12733   }
12734
12735   return TRUE;
12736 }
12737
12738 void ScrollLevel(int dx, int dy)
12739 {
12740   int scroll_offset = 2 * TILEX_VAR;
12741   int x, y;
12742
12743   BlitBitmap(drawto_field, drawto_field,
12744              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12745              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12746              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12747              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12748              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12749              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12750
12751   if (dx != 0)
12752   {
12753     x = (dx == 1 ? BX1 : BX2);
12754     for (y = BY1; y <= BY2; y++)
12755       DrawScreenField(x, y);
12756   }
12757
12758   if (dy != 0)
12759   {
12760     y = (dy == 1 ? BY1 : BY2);
12761     for (x = BX1; x <= BX2; x++)
12762       DrawScreenField(x, y);
12763   }
12764
12765   redraw_mask |= REDRAW_FIELD;
12766 }
12767
12768 static boolean canFallDown(struct PlayerInfo *player)
12769 {
12770   int jx = player->jx, jy = player->jy;
12771
12772   return (IN_LEV_FIELD(jx, jy + 1) &&
12773           (IS_FREE(jx, jy + 1) ||
12774            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12775           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12776           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12777 }
12778
12779 static boolean canPassField(int x, int y, int move_dir)
12780 {
12781   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12782   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12783   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12784   int nextx = x + dx;
12785   int nexty = y + dy;
12786   int element = Tile[x][y];
12787
12788   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12789           !CAN_MOVE(element) &&
12790           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12791           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12792           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12793 }
12794
12795 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12796 {
12797   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12798   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12799   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12800   int newx = x + dx;
12801   int newy = y + dy;
12802
12803   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12804           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12805           (IS_DIGGABLE(Tile[newx][newy]) ||
12806            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12807            canPassField(newx, newy, move_dir)));
12808 }
12809
12810 static void CheckGravityMovement(struct PlayerInfo *player)
12811 {
12812   if (player->gravity && !player->programmed_action)
12813   {
12814     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12815     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12816     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12817     int jx = player->jx, jy = player->jy;
12818     boolean player_is_moving_to_valid_field =
12819       (!player_is_snapping &&
12820        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12821         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12822     boolean player_can_fall_down = canFallDown(player);
12823
12824     if (player_can_fall_down &&
12825         !player_is_moving_to_valid_field)
12826       player->programmed_action = MV_DOWN;
12827   }
12828 }
12829
12830 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12831 {
12832   return CheckGravityMovement(player);
12833
12834   if (player->gravity && !player->programmed_action)
12835   {
12836     int jx = player->jx, jy = player->jy;
12837     boolean field_under_player_is_free =
12838       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12839     boolean player_is_standing_on_valid_field =
12840       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12841        (IS_WALKABLE(Tile[jx][jy]) &&
12842         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12843
12844     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12845       player->programmed_action = MV_DOWN;
12846   }
12847 }
12848
12849 /*
12850   MovePlayerOneStep()
12851   -----------------------------------------------------------------------------
12852   dx, dy:               direction (non-diagonal) to try to move the player to
12853   real_dx, real_dy:     direction as read from input device (can be diagonal)
12854 */
12855
12856 boolean MovePlayerOneStep(struct PlayerInfo *player,
12857                           int dx, int dy, int real_dx, int real_dy)
12858 {
12859   int jx = player->jx, jy = player->jy;
12860   int new_jx = jx + dx, new_jy = jy + dy;
12861   int can_move;
12862   boolean player_can_move = !player->cannot_move;
12863
12864   if (!player->active || (!dx && !dy))
12865     return MP_NO_ACTION;
12866
12867   player->MovDir = (dx < 0 ? MV_LEFT :
12868                     dx > 0 ? MV_RIGHT :
12869                     dy < 0 ? MV_UP :
12870                     dy > 0 ? MV_DOWN :  MV_NONE);
12871
12872   if (!IN_LEV_FIELD(new_jx, new_jy))
12873     return MP_NO_ACTION;
12874
12875   if (!player_can_move)
12876   {
12877     if (player->MovPos == 0)
12878     {
12879       player->is_moving = FALSE;
12880       player->is_digging = FALSE;
12881       player->is_collecting = FALSE;
12882       player->is_snapping = FALSE;
12883       player->is_pushing = FALSE;
12884     }
12885   }
12886
12887   if (!network.enabled && game.centered_player_nr == -1 &&
12888       !AllPlayersInSight(player, new_jx, new_jy))
12889     return MP_NO_ACTION;
12890
12891   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx, real_dy, DF_DIG);
12892   if (can_move != MP_MOVING)
12893     return can_move;
12894
12895   // check if DigField() has caused relocation of the player
12896   if (player->jx != jx || player->jy != jy)
12897     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12898
12899   StorePlayer[jx][jy] = 0;
12900   player->last_jx = jx;
12901   player->last_jy = jy;
12902   player->jx = new_jx;
12903   player->jy = new_jy;
12904   StorePlayer[new_jx][new_jy] = player->element_nr;
12905
12906   if (player->move_delay_value_next != -1)
12907   {
12908     player->move_delay_value = player->move_delay_value_next;
12909     player->move_delay_value_next = -1;
12910   }
12911
12912   player->MovPos =
12913     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12914
12915   player->step_counter++;
12916
12917   PlayerVisit[jx][jy] = FrameCounter;
12918
12919   player->is_moving = TRUE;
12920
12921 #if 1
12922   // should better be called in MovePlayer(), but this breaks some tapes
12923   ScrollPlayer(player, SCROLL_INIT);
12924 #endif
12925
12926   return MP_MOVING;
12927 }
12928
12929 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12930 {
12931   int jx = player->jx, jy = player->jy;
12932   int old_jx = jx, old_jy = jy;
12933   int moved = MP_NO_ACTION;
12934
12935   if (!player->active)
12936     return FALSE;
12937
12938   if (!dx && !dy)
12939   {
12940     if (player->MovPos == 0)
12941     {
12942       player->is_moving = FALSE;
12943       player->is_digging = FALSE;
12944       player->is_collecting = FALSE;
12945       player->is_snapping = FALSE;
12946       player->is_pushing = FALSE;
12947     }
12948
12949     return FALSE;
12950   }
12951
12952   if (player->move_delay > 0)
12953     return FALSE;
12954
12955   player->move_delay = -1;              // set to "uninitialized" value
12956
12957   // store if player is automatically moved to next field
12958   player->is_auto_moving = (player->programmed_action != MV_NONE);
12959
12960   // remove the last programmed player action
12961   player->programmed_action = 0;
12962
12963   if (player->MovPos)
12964   {
12965     // should only happen if pre-1.2 tape recordings are played
12966     // this is only for backward compatibility
12967
12968     int original_move_delay_value = player->move_delay_value;
12969
12970 #if DEBUG
12971     Debug("game:playing:MovePlayer",
12972           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12973           tape.counter);
12974 #endif
12975
12976     // scroll remaining steps with finest movement resolution
12977     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12978
12979     while (player->MovPos)
12980     {
12981       ScrollPlayer(player, SCROLL_GO_ON);
12982       ScrollScreen(NULL, SCROLL_GO_ON);
12983
12984       AdvanceFrameAndPlayerCounters(player->index_nr);
12985
12986       DrawAllPlayers();
12987       BackToFront_WithFrameDelay(0);
12988     }
12989
12990     player->move_delay_value = original_move_delay_value;
12991   }
12992
12993   player->is_active = FALSE;
12994
12995   if (player->last_move_dir & MV_HORIZONTAL)
12996   {
12997     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12998       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12999   }
13000   else
13001   {
13002     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13003       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13004   }
13005
13006   if (!moved && !player->is_active)
13007   {
13008     player->is_moving = FALSE;
13009     player->is_digging = FALSE;
13010     player->is_collecting = FALSE;
13011     player->is_snapping = FALSE;
13012     player->is_pushing = FALSE;
13013   }
13014
13015   jx = player->jx;
13016   jy = player->jy;
13017
13018   if (moved & MP_MOVING && !ScreenMovPos &&
13019       (player->index_nr == game.centered_player_nr ||
13020        game.centered_player_nr == -1))
13021   {
13022     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13023
13024     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13025     {
13026       // actual player has left the screen -- scroll in that direction
13027       if (jx != old_jx)         // player has moved horizontally
13028         scroll_x += (jx - old_jx);
13029       else                      // player has moved vertically
13030         scroll_y += (jy - old_jy);
13031     }
13032     else
13033     {
13034       int offset_raw = game.scroll_delay_value;
13035
13036       if (jx != old_jx)         // player has moved horizontally
13037       {
13038         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
13039         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
13040         int new_scroll_x = jx - MIDPOSX + offset_x;
13041
13042         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
13043             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
13044           scroll_x = new_scroll_x;
13045
13046         // don't scroll over playfield boundaries
13047         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
13048
13049         // don't scroll more than one field at a time
13050         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13051
13052         // don't scroll against the player's moving direction
13053         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13054             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13055           scroll_x = old_scroll_x;
13056       }
13057       else                      // player has moved vertically
13058       {
13059         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
13060         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
13061         int new_scroll_y = jy - MIDPOSY + offset_y;
13062
13063         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
13064             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13065           scroll_y = new_scroll_y;
13066
13067         // don't scroll over playfield boundaries
13068         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13069
13070         // don't scroll more than one field at a time
13071         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13072
13073         // don't scroll against the player's moving direction
13074         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13075             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13076           scroll_y = old_scroll_y;
13077       }
13078     }
13079
13080     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13081     {
13082       if (!network.enabled && game.centered_player_nr == -1 &&
13083           !AllPlayersInVisibleScreen())
13084       {
13085         scroll_x = old_scroll_x;
13086         scroll_y = old_scroll_y;
13087       }
13088       else
13089       {
13090         ScrollScreen(player, SCROLL_INIT);
13091         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13092       }
13093     }
13094   }
13095
13096   player->StepFrame = 0;
13097
13098   if (moved & MP_MOVING)
13099   {
13100     if (old_jx != jx && old_jy == jy)
13101       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13102     else if (old_jx == jx && old_jy != jy)
13103       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13104
13105     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13106
13107     player->last_move_dir = player->MovDir;
13108     player->is_moving = TRUE;
13109     player->is_snapping = FALSE;
13110     player->is_switching = FALSE;
13111     player->is_dropping = FALSE;
13112     player->is_dropping_pressed = FALSE;
13113     player->drop_pressed_delay = 0;
13114
13115 #if 0
13116     // should better be called here than above, but this breaks some tapes
13117     ScrollPlayer(player, SCROLL_INIT);
13118 #endif
13119   }
13120   else
13121   {
13122     CheckGravityMovementWhenNotMoving(player);
13123
13124     player->is_moving = FALSE;
13125
13126     /* at this point, the player is allowed to move, but cannot move right now
13127        (e.g. because of something blocking the way) -- ensure that the player
13128        is also allowed to move in the next frame (in old versions before 3.1.1,
13129        the player was forced to wait again for eight frames before next try) */
13130
13131     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13132       player->move_delay = 0;   // allow direct movement in the next frame
13133   }
13134
13135   if (player->move_delay == -1)         // not yet initialized by DigField()
13136     player->move_delay = player->move_delay_value;
13137
13138   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13139   {
13140     TestIfPlayerTouchesBadThing(jx, jy);
13141     TestIfPlayerTouchesCustomElement(jx, jy);
13142   }
13143
13144   if (!player->active)
13145     RemovePlayer(player);
13146
13147   return moved;
13148 }
13149
13150 void ScrollPlayer(struct PlayerInfo *player, int mode)
13151 {
13152   int jx = player->jx, jy = player->jy;
13153   int last_jx = player->last_jx, last_jy = player->last_jy;
13154   int move_stepsize = TILEX / player->move_delay_value;
13155
13156   if (!player->active)
13157     return;
13158
13159   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13160     return;
13161
13162   if (mode == SCROLL_INIT)
13163   {
13164     player->actual_frame_counter.count = FrameCounter;
13165     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13166
13167     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13168         Tile[last_jx][last_jy] == EL_EMPTY)
13169     {
13170       int last_field_block_delay = 0;   // start with no blocking at all
13171       int block_delay_adjustment = player->block_delay_adjustment;
13172
13173       // if player blocks last field, add delay for exactly one move
13174       if (player->block_last_field)
13175       {
13176         last_field_block_delay += player->move_delay_value;
13177
13178         // when blocking enabled, prevent moving up despite gravity
13179         if (player->gravity && player->MovDir == MV_UP)
13180           block_delay_adjustment = -1;
13181       }
13182
13183       // add block delay adjustment (also possible when not blocking)
13184       last_field_block_delay += block_delay_adjustment;
13185
13186       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13187       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13188     }
13189
13190     if (player->MovPos != 0)    // player has not yet reached destination
13191       return;
13192   }
13193   else if (!FrameReached(&player->actual_frame_counter))
13194     return;
13195
13196   if (player->MovPos != 0)
13197   {
13198     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13199     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13200
13201     // before DrawPlayer() to draw correct player graphic for this case
13202     if (player->MovPos == 0)
13203       CheckGravityMovement(player);
13204   }
13205
13206   if (player->MovPos == 0)      // player reached destination field
13207   {
13208     if (player->move_delay_reset_counter > 0)
13209     {
13210       player->move_delay_reset_counter--;
13211
13212       if (player->move_delay_reset_counter == 0)
13213       {
13214         // continue with normal speed after quickly moving through gate
13215         HALVE_PLAYER_SPEED(player);
13216
13217         // be able to make the next move without delay
13218         player->move_delay = 0;
13219       }
13220     }
13221
13222     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13223         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13224         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13225         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13226         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13227         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13228         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13229         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13230     {
13231       ExitPlayer(player);
13232
13233       if (game.players_still_needed == 0 &&
13234           (game.friends_still_needed == 0 ||
13235            IS_SP_ELEMENT(Tile[jx][jy])))
13236         LevelSolved();
13237     }
13238
13239     player->last_jx = jx;
13240     player->last_jy = jy;
13241
13242     // this breaks one level: "machine", level 000
13243     {
13244       int move_direction = player->MovDir;
13245       int enter_side = MV_DIR_OPPOSITE(move_direction);
13246       int leave_side = move_direction;
13247       int old_jx = last_jx;
13248       int old_jy = last_jy;
13249       int old_element = Tile[old_jx][old_jy];
13250       int new_element = Tile[jx][jy];
13251
13252       if (IS_CUSTOM_ELEMENT(old_element))
13253         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13254                                    CE_LEFT_BY_PLAYER,
13255                                    player->index_bit, leave_side);
13256
13257       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13258                                           CE_PLAYER_LEAVES_X,
13259                                           player->index_bit, leave_side);
13260
13261       // needed because pushed element has not yet reached its destination,
13262       // so it would trigger a change event at its previous field location
13263       if (!player->is_pushing)
13264       {
13265         if (IS_CUSTOM_ELEMENT(new_element))
13266           CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13267                                      player->index_bit, enter_side);
13268
13269         CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13270                                             CE_PLAYER_ENTERS_X,
13271                                             player->index_bit, enter_side);
13272       }
13273
13274       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13275                                         CE_MOVE_OF_X, move_direction);
13276     }
13277
13278     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13279     {
13280       TestIfPlayerTouchesBadThing(jx, jy);
13281       TestIfPlayerTouchesCustomElement(jx, jy);
13282
13283       // needed because pushed element has not yet reached its destination,
13284       // so it would trigger a change event at its previous field location
13285       if (!player->is_pushing)
13286         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13287
13288       if (level.finish_dig_collect &&
13289           (player->is_digging || player->is_collecting))
13290       {
13291         int last_element = player->last_removed_element;
13292         int move_direction = player->MovDir;
13293         int enter_side = MV_DIR_OPPOSITE(move_direction);
13294         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13295                             CE_PLAYER_COLLECTS_X);
13296
13297         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13298                                             player->index_bit, enter_side);
13299
13300         player->last_removed_element = EL_UNDEFINED;
13301       }
13302
13303       if (!player->active)
13304         RemovePlayer(player);
13305     }
13306
13307     if (level.use_step_counter)
13308       CheckLevelTime_StepCounter();
13309
13310     if (tape.single_step && tape.recording && !tape.pausing &&
13311         !player->programmed_action)
13312       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13313
13314     if (!player->programmed_action)
13315       CheckSaveEngineSnapshot(player);
13316   }
13317 }
13318
13319 void ScrollScreen(struct PlayerInfo *player, int mode)
13320 {
13321   static DelayCounter screen_frame_counter = { 0 };
13322
13323   if (mode == SCROLL_INIT)
13324   {
13325     // set scrolling step size according to actual player's moving speed
13326     ScrollStepSize = TILEX / player->move_delay_value;
13327
13328     screen_frame_counter.count = FrameCounter;
13329     screen_frame_counter.value = 1;
13330
13331     ScreenMovDir = player->MovDir;
13332     ScreenMovPos = player->MovPos;
13333     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13334     return;
13335   }
13336   else if (!FrameReached(&screen_frame_counter))
13337     return;
13338
13339   if (ScreenMovPos)
13340   {
13341     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13342     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13343     redraw_mask |= REDRAW_FIELD;
13344   }
13345   else
13346     ScreenMovDir = MV_NONE;
13347 }
13348
13349 void CheckNextToConditions(int x, int y)
13350 {
13351   int element = Tile[x][y];
13352
13353   if (IS_PLAYER(x, y))
13354     TestIfPlayerNextToCustomElement(x, y);
13355
13356   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13357       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13358     TestIfElementNextToCustomElement(x, y);
13359 }
13360
13361 void TestIfPlayerNextToCustomElement(int x, int y)
13362 {
13363   struct XY *xy = xy_topdown;
13364   static int trigger_sides[4][2] =
13365   {
13366     // center side       border side
13367     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13368     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13369     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13370     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13371   };
13372   int i;
13373
13374   if (!IS_PLAYER(x, y))
13375     return;
13376
13377   struct PlayerInfo *player = PLAYERINFO(x, y);
13378
13379   if (player->is_moving)
13380     return;
13381
13382   for (i = 0; i < NUM_DIRECTIONS; i++)
13383   {
13384     int xx = x + xy[i].x;
13385     int yy = y + xy[i].y;
13386     int border_side = trigger_sides[i][1];
13387     int border_element;
13388
13389     if (!IN_LEV_FIELD(xx, yy))
13390       continue;
13391
13392     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13393       continue;         // center and border element not connected
13394
13395     border_element = Tile[xx][yy];
13396
13397     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13398                                player->index_bit, border_side);
13399     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13400                                         CE_PLAYER_NEXT_TO_X,
13401                                         player->index_bit, border_side);
13402
13403     /* use player element that is initially defined in the level playfield,
13404        not the player element that corresponds to the runtime player number
13405        (example: a level that contains EL_PLAYER_3 as the only player would
13406        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13407
13408     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13409                              CE_NEXT_TO_X, border_side);
13410   }
13411 }
13412
13413 void TestIfPlayerTouchesCustomElement(int x, int y)
13414 {
13415   struct XY *xy = xy_topdown;
13416   static int trigger_sides[4][2] =
13417   {
13418     // center side       border side
13419     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13420     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13421     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13422     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13423   };
13424   static int touch_dir[4] =
13425   {
13426     MV_LEFT | MV_RIGHT,
13427     MV_UP   | MV_DOWN,
13428     MV_UP   | MV_DOWN,
13429     MV_LEFT | MV_RIGHT
13430   };
13431   int center_element = Tile[x][y];      // should always be non-moving!
13432   int i;
13433
13434   for (i = 0; i < NUM_DIRECTIONS; i++)
13435   {
13436     int xx = x + xy[i].x;
13437     int yy = y + xy[i].y;
13438     int center_side = trigger_sides[i][0];
13439     int border_side = trigger_sides[i][1];
13440     int border_element;
13441
13442     if (!IN_LEV_FIELD(xx, yy))
13443       continue;
13444
13445     if (IS_PLAYER(x, y))                // player found at center element
13446     {
13447       struct PlayerInfo *player = PLAYERINFO(x, y);
13448
13449       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13450         border_element = Tile[xx][yy];          // may be moving!
13451       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13452         border_element = Tile[xx][yy];
13453       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13454         border_element = MovingOrBlocked2Element(xx, yy);
13455       else
13456         continue;               // center and border element do not touch
13457
13458       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13459                                  player->index_bit, border_side);
13460       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13461                                           CE_PLAYER_TOUCHES_X,
13462                                           player->index_bit, border_side);
13463
13464       {
13465         /* use player element that is initially defined in the level playfield,
13466            not the player element that corresponds to the runtime player number
13467            (example: a level that contains EL_PLAYER_3 as the only player would
13468            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13469         int player_element = PLAYERINFO(x, y)->initial_element;
13470
13471         CheckElementChangeBySide(xx, yy, border_element, player_element,
13472                                  CE_TOUCHING_X, border_side);
13473       }
13474     }
13475     else if (IS_PLAYER(xx, yy))         // player found at border element
13476     {
13477       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13478
13479       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13480       {
13481         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13482           continue;             // center and border element do not touch
13483       }
13484
13485       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13486                                  player->index_bit, center_side);
13487       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13488                                           CE_PLAYER_TOUCHES_X,
13489                                           player->index_bit, center_side);
13490
13491       {
13492         /* use player element that is initially defined in the level playfield,
13493            not the player element that corresponds to the runtime player number
13494            (example: a level that contains EL_PLAYER_3 as the only player would
13495            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13496         int player_element = PLAYERINFO(xx, yy)->initial_element;
13497
13498         CheckElementChangeBySide(x, y, center_element, player_element,
13499                                  CE_TOUCHING_X, center_side);
13500       }
13501
13502       break;
13503     }
13504   }
13505 }
13506
13507 void TestIfElementNextToCustomElement(int x, int y)
13508 {
13509   struct XY *xy = xy_topdown;
13510   static int trigger_sides[4][2] =
13511   {
13512     // center side      border side
13513     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13514     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13515     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13516     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13517   };
13518   int center_element = Tile[x][y];      // should always be non-moving!
13519   int i;
13520
13521   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13522     return;
13523
13524   for (i = 0; i < NUM_DIRECTIONS; i++)
13525   {
13526     int xx = x + xy[i].x;
13527     int yy = y + xy[i].y;
13528     int border_side = trigger_sides[i][1];
13529     int border_element;
13530
13531     if (!IN_LEV_FIELD(xx, yy))
13532       continue;
13533
13534     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13535       continue;                 // center and border element not connected
13536
13537     border_element = Tile[xx][yy];
13538
13539     // check for change of center element (but change it only once)
13540     if (CheckElementChangeBySide(x, y, center_element, border_element,
13541                                  CE_NEXT_TO_X, border_side))
13542       break;
13543   }
13544 }
13545
13546 void TestIfElementTouchesCustomElement(int x, int y)
13547 {
13548   struct XY *xy = xy_topdown;
13549   static int trigger_sides[4][2] =
13550   {
13551     // center side      border side
13552     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13553     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13554     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13555     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13556   };
13557   static int touch_dir[4] =
13558   {
13559     MV_LEFT | MV_RIGHT,
13560     MV_UP   | MV_DOWN,
13561     MV_UP   | MV_DOWN,
13562     MV_LEFT | MV_RIGHT
13563   };
13564   boolean change_center_element = FALSE;
13565   int center_element = Tile[x][y];      // should always be non-moving!
13566   int border_element_old[NUM_DIRECTIONS];
13567   int i;
13568
13569   for (i = 0; i < NUM_DIRECTIONS; i++)
13570   {
13571     int xx = x + xy[i].x;
13572     int yy = y + xy[i].y;
13573     int border_element;
13574
13575     border_element_old[i] = -1;
13576
13577     if (!IN_LEV_FIELD(xx, yy))
13578       continue;
13579
13580     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13581       border_element = Tile[xx][yy];    // may be moving!
13582     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13583       border_element = Tile[xx][yy];
13584     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13585       border_element = MovingOrBlocked2Element(xx, yy);
13586     else
13587       continue;                 // center and border element do not touch
13588
13589     border_element_old[i] = border_element;
13590   }
13591
13592   for (i = 0; i < NUM_DIRECTIONS; i++)
13593   {
13594     int xx = x + xy[i].x;
13595     int yy = y + xy[i].y;
13596     int center_side = trigger_sides[i][0];
13597     int border_element = border_element_old[i];
13598
13599     if (border_element == -1)
13600       continue;
13601
13602     // check for change of border element
13603     CheckElementChangeBySide(xx, yy, border_element, center_element,
13604                              CE_TOUCHING_X, center_side);
13605
13606     // (center element cannot be player, so we dont have to check this here)
13607   }
13608
13609   for (i = 0; i < NUM_DIRECTIONS; i++)
13610   {
13611     int xx = x + xy[i].x;
13612     int yy = y + xy[i].y;
13613     int border_side = trigger_sides[i][1];
13614     int border_element = border_element_old[i];
13615
13616     if (border_element == -1)
13617       continue;
13618
13619     // check for change of center element (but change it only once)
13620     if (!change_center_element)
13621       change_center_element =
13622         CheckElementChangeBySide(x, y, center_element, border_element,
13623                                  CE_TOUCHING_X, border_side);
13624
13625     if (IS_PLAYER(xx, yy))
13626     {
13627       /* use player element that is initially defined in the level playfield,
13628          not the player element that corresponds to the runtime player number
13629          (example: a level that contains EL_PLAYER_3 as the only player would
13630          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13631       int player_element = PLAYERINFO(xx, yy)->initial_element;
13632
13633       CheckElementChangeBySide(x, y, center_element, player_element,
13634                                CE_TOUCHING_X, border_side);
13635     }
13636   }
13637 }
13638
13639 void TestIfElementHitsCustomElement(int x, int y, int direction)
13640 {
13641   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13642   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13643   int hitx = x + dx, hity = y + dy;
13644   int hitting_element = Tile[x][y];
13645   int touched_element;
13646
13647   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13648     return;
13649
13650   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13651                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13652
13653   if (IN_LEV_FIELD(hitx, hity))
13654   {
13655     int opposite_direction = MV_DIR_OPPOSITE(direction);
13656     int hitting_side = direction;
13657     int touched_side = opposite_direction;
13658     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13659                           MovDir[hitx][hity] != direction ||
13660                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13661
13662     object_hit = TRUE;
13663
13664     if (object_hit)
13665     {
13666       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13667                                CE_HITTING_X, touched_side);
13668
13669       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13670                                CE_HIT_BY_X, hitting_side);
13671
13672       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13673                                CE_HIT_BY_SOMETHING, opposite_direction);
13674
13675       if (IS_PLAYER(hitx, hity))
13676       {
13677         /* use player element that is initially defined in the level playfield,
13678            not the player element that corresponds to the runtime player number
13679            (example: a level that contains EL_PLAYER_3 as the only player would
13680            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13681         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13682
13683         CheckElementChangeBySide(x, y, hitting_element, player_element,
13684                                  CE_HITTING_X, touched_side);
13685       }
13686     }
13687   }
13688
13689   // "hitting something" is also true when hitting the playfield border
13690   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13691                            CE_HITTING_SOMETHING, direction);
13692 }
13693
13694 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13695 {
13696   int i, kill_x = -1, kill_y = -1;
13697
13698   int bad_element = -1;
13699   struct XY *test_xy = xy_topdown;
13700   static int test_dir[4] =
13701   {
13702     MV_UP,
13703     MV_LEFT,
13704     MV_RIGHT,
13705     MV_DOWN
13706   };
13707
13708   for (i = 0; i < NUM_DIRECTIONS; i++)
13709   {
13710     int test_x, test_y, test_move_dir, test_element;
13711
13712     test_x = good_x + test_xy[i].x;
13713     test_y = good_y + test_xy[i].y;
13714
13715     if (!IN_LEV_FIELD(test_x, test_y))
13716       continue;
13717
13718     test_move_dir =
13719       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13720
13721     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13722
13723     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13724        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13725     */
13726     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13727         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13728     {
13729       kill_x = test_x;
13730       kill_y = test_y;
13731       bad_element = test_element;
13732
13733       break;
13734     }
13735   }
13736
13737   if (kill_x != -1 || kill_y != -1)
13738   {
13739     if (IS_PLAYER(good_x, good_y))
13740     {
13741       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13742
13743       if (player->shield_deadly_time_left > 0 &&
13744           !IS_INDESTRUCTIBLE(bad_element))
13745         Bang(kill_x, kill_y);
13746       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13747         KillPlayer(player);
13748     }
13749     else
13750       Bang(good_x, good_y);
13751   }
13752 }
13753
13754 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13755 {
13756   int i, kill_x = -1, kill_y = -1;
13757   int bad_element = Tile[bad_x][bad_y];
13758   struct XY *test_xy = xy_topdown;
13759   static int touch_dir[4] =
13760   {
13761     MV_LEFT | MV_RIGHT,
13762     MV_UP   | MV_DOWN,
13763     MV_UP   | MV_DOWN,
13764     MV_LEFT | MV_RIGHT
13765   };
13766   static int test_dir[4] =
13767   {
13768     MV_UP,
13769     MV_LEFT,
13770     MV_RIGHT,
13771     MV_DOWN
13772   };
13773
13774   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13775     return;
13776
13777   for (i = 0; i < NUM_DIRECTIONS; i++)
13778   {
13779     int test_x, test_y, test_move_dir, test_element;
13780
13781     test_x = bad_x + test_xy[i].x;
13782     test_y = bad_y + test_xy[i].y;
13783
13784     if (!IN_LEV_FIELD(test_x, test_y))
13785       continue;
13786
13787     test_move_dir =
13788       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13789
13790     test_element = Tile[test_x][test_y];
13791
13792     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13793        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13794     */
13795     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13796         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13797     {
13798       // good thing is player or penguin that does not move away
13799       if (IS_PLAYER(test_x, test_y))
13800       {
13801         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13802
13803         if (bad_element == EL_ROBOT && player->is_moving)
13804           continue;     // robot does not kill player if he is moving
13805
13806         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13807         {
13808           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13809             continue;           // center and border element do not touch
13810         }
13811
13812         kill_x = test_x;
13813         kill_y = test_y;
13814
13815         break;
13816       }
13817       else if (test_element == EL_PENGUIN)
13818       {
13819         kill_x = test_x;
13820         kill_y = test_y;
13821
13822         break;
13823       }
13824     }
13825   }
13826
13827   if (kill_x != -1 || kill_y != -1)
13828   {
13829     if (IS_PLAYER(kill_x, kill_y))
13830     {
13831       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13832
13833       if (player->shield_deadly_time_left > 0 &&
13834           !IS_INDESTRUCTIBLE(bad_element))
13835         Bang(bad_x, bad_y);
13836       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13837         KillPlayer(player);
13838     }
13839     else
13840       Bang(kill_x, kill_y);
13841   }
13842 }
13843
13844 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13845 {
13846   int bad_element = Tile[bad_x][bad_y];
13847   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13848   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13849   int test_x = bad_x + dx, test_y = bad_y + dy;
13850   int test_move_dir, test_element;
13851   int kill_x = -1, kill_y = -1;
13852
13853   if (!IN_LEV_FIELD(test_x, test_y))
13854     return;
13855
13856   test_move_dir =
13857     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13858
13859   test_element = Tile[test_x][test_y];
13860
13861   if (test_move_dir != bad_move_dir)
13862   {
13863     // good thing can be player or penguin that does not move away
13864     if (IS_PLAYER(test_x, test_y))
13865     {
13866       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13867
13868       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13869          player as being hit when he is moving towards the bad thing, because
13870          the "get hit by" condition would be lost after the player stops) */
13871       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13872         return;         // player moves away from bad thing
13873
13874       kill_x = test_x;
13875       kill_y = test_y;
13876     }
13877     else if (test_element == EL_PENGUIN)
13878     {
13879       kill_x = test_x;
13880       kill_y = test_y;
13881     }
13882   }
13883
13884   if (kill_x != -1 || kill_y != -1)
13885   {
13886     if (IS_PLAYER(kill_x, kill_y))
13887     {
13888       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13889
13890       if (player->shield_deadly_time_left > 0 &&
13891           !IS_INDESTRUCTIBLE(bad_element))
13892         Bang(bad_x, bad_y);
13893       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13894         KillPlayer(player);
13895     }
13896     else
13897       Bang(kill_x, kill_y);
13898   }
13899 }
13900
13901 void TestIfPlayerTouchesBadThing(int x, int y)
13902 {
13903   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13904 }
13905
13906 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13907 {
13908   TestIfGoodThingHitsBadThing(x, y, move_dir);
13909 }
13910
13911 void TestIfBadThingTouchesPlayer(int x, int y)
13912 {
13913   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13914 }
13915
13916 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13917 {
13918   TestIfBadThingHitsGoodThing(x, y, move_dir);
13919 }
13920
13921 void TestIfFriendTouchesBadThing(int x, int y)
13922 {
13923   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13924 }
13925
13926 void TestIfBadThingTouchesFriend(int x, int y)
13927 {
13928   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13929 }
13930
13931 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13932 {
13933   int i, kill_x = bad_x, kill_y = bad_y;
13934   struct XY *xy = xy_topdown;
13935
13936   for (i = 0; i < NUM_DIRECTIONS; i++)
13937   {
13938     int x, y, element;
13939
13940     x = bad_x + xy[i].x;
13941     y = bad_y + xy[i].y;
13942     if (!IN_LEV_FIELD(x, y))
13943       continue;
13944
13945     element = Tile[x][y];
13946     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13947         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13948     {
13949       kill_x = x;
13950       kill_y = y;
13951       break;
13952     }
13953   }
13954
13955   if (kill_x != bad_x || kill_y != bad_y)
13956     Bang(bad_x, bad_y);
13957 }
13958
13959 void KillPlayer(struct PlayerInfo *player)
13960 {
13961   int jx = player->jx, jy = player->jy;
13962
13963   if (!player->active)
13964     return;
13965
13966 #if 0
13967   Debug("game:playing:KillPlayer",
13968         "0: killed == %d, active == %d, reanimated == %d",
13969         player->killed, player->active, player->reanimated);
13970 #endif
13971
13972   /* the following code was introduced to prevent an infinite loop when calling
13973      -> Bang()
13974      -> CheckTriggeredElementChangeExt()
13975      -> ExecuteCustomElementAction()
13976      -> KillPlayer()
13977      -> (infinitely repeating the above sequence of function calls)
13978      which occurs when killing the player while having a CE with the setting
13979      "kill player X when explosion of <player X>"; the solution using a new
13980      field "player->killed" was chosen for backwards compatibility, although
13981      clever use of the fields "player->active" etc. would probably also work */
13982 #if 1
13983   if (player->killed)
13984     return;
13985 #endif
13986
13987   player->killed = TRUE;
13988
13989   // remove accessible field at the player's position
13990   RemoveField(jx, jy);
13991
13992   // deactivate shield (else Bang()/Explode() would not work right)
13993   player->shield_normal_time_left = 0;
13994   player->shield_deadly_time_left = 0;
13995
13996 #if 0
13997   Debug("game:playing:KillPlayer",
13998         "1: killed == %d, active == %d, reanimated == %d",
13999         player->killed, player->active, player->reanimated);
14000 #endif
14001
14002   Bang(jx, jy);
14003
14004 #if 0
14005   Debug("game:playing:KillPlayer",
14006         "2: killed == %d, active == %d, reanimated == %d",
14007         player->killed, player->active, player->reanimated);
14008 #endif
14009
14010   if (player->reanimated)       // killed player may have been reanimated
14011     player->killed = player->reanimated = FALSE;
14012   else
14013     BuryPlayer(player);
14014 }
14015
14016 static void KillPlayerUnlessEnemyProtected(int x, int y)
14017 {
14018   if (!PLAYER_ENEMY_PROTECTED(x, y))
14019     KillPlayer(PLAYERINFO(x, y));
14020 }
14021
14022 static void KillPlayerUnlessExplosionProtected(int x, int y)
14023 {
14024   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14025     KillPlayer(PLAYERINFO(x, y));
14026 }
14027
14028 void BuryPlayer(struct PlayerInfo *player)
14029 {
14030   int jx = player->jx, jy = player->jy;
14031
14032   if (!player->active)
14033     return;
14034
14035   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14036
14037   RemovePlayer(player);
14038
14039   player->buried = TRUE;
14040
14041   if (game.all_players_gone)
14042     game.GameOver = TRUE;
14043 }
14044
14045 void RemovePlayer(struct PlayerInfo *player)
14046 {
14047   int jx = player->jx, jy = player->jy;
14048   int i, found = FALSE;
14049
14050   player->present = FALSE;
14051   player->active = FALSE;
14052
14053   // required for some CE actions (even if the player is not active anymore)
14054   player->MovPos = 0;
14055
14056   if (!ExplodeField[jx][jy])
14057     StorePlayer[jx][jy] = 0;
14058
14059   if (player->is_moving)
14060     TEST_DrawLevelField(player->last_jx, player->last_jy);
14061
14062   for (i = 0; i < MAX_PLAYERS; i++)
14063     if (stored_player[i].active)
14064       found = TRUE;
14065
14066   if (!found)
14067   {
14068     game.all_players_gone = TRUE;
14069     game.GameOver = TRUE;
14070   }
14071
14072   game.exit_x = game.robot_wheel_x = jx;
14073   game.exit_y = game.robot_wheel_y = jy;
14074 }
14075
14076 void ExitPlayer(struct PlayerInfo *player)
14077 {
14078   DrawPlayer(player);   // needed here only to cleanup last field
14079   RemovePlayer(player);
14080
14081   if (game.players_still_needed > 0)
14082     game.players_still_needed--;
14083 }
14084
14085 static void SetFieldForSnapping(int x, int y, int element, int direction,
14086                                 int player_index_bit)
14087 {
14088   struct ElementInfo *ei = &element_info[element];
14089   int direction_bit = MV_DIR_TO_BIT(direction);
14090   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14091   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14092                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14093
14094   Tile[x][y] = EL_ELEMENT_SNAPPING;
14095   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14096   MovDir[x][y] = direction;
14097   Store[x][y] = element;
14098   Store2[x][y] = player_index_bit;
14099
14100   ResetGfxAnimation(x, y);
14101
14102   GfxElement[x][y] = element;
14103   GfxAction[x][y] = action;
14104   GfxDir[x][y] = direction;
14105   GfxFrame[x][y] = -1;
14106 }
14107
14108 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14109                                    int player_index_bit)
14110 {
14111   TestIfElementTouchesCustomElement(x, y);      // for empty space
14112
14113   if (level.finish_dig_collect)
14114   {
14115     int dig_side = MV_DIR_OPPOSITE(direction);
14116     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14117                         CE_PLAYER_COLLECTS_X);
14118
14119     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14120                                         player_index_bit, dig_side);
14121     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14122                                         player_index_bit, dig_side);
14123   }
14124 }
14125
14126 /*
14127   =============================================================================
14128   checkDiagonalPushing()
14129   -----------------------------------------------------------------------------
14130   check if diagonal input device direction results in pushing of object
14131   (by checking if the alternative direction is walkable, diggable, ...)
14132   =============================================================================
14133 */
14134
14135 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14136                                     int x, int y, int real_dx, int real_dy)
14137 {
14138   int jx, jy, dx, dy, xx, yy;
14139
14140   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14141     return TRUE;
14142
14143   // diagonal direction: check alternative direction
14144   jx = player->jx;
14145   jy = player->jy;
14146   dx = x - jx;
14147   dy = y - jy;
14148   xx = jx + (dx == 0 ? real_dx : 0);
14149   yy = jy + (dy == 0 ? real_dy : 0);
14150
14151   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14152 }
14153
14154 /*
14155   =============================================================================
14156   DigField()
14157   -----------------------------------------------------------------------------
14158   x, y:                 field next to player (non-diagonal) to try to dig to
14159   real_dx, real_dy:     direction as read from input device (can be diagonal)
14160   =============================================================================
14161 */
14162
14163 static int DigField(struct PlayerInfo *player,
14164                     int oldx, int oldy, int x, int y,
14165                     int real_dx, int real_dy, int mode)
14166 {
14167   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14168   boolean player_was_pushing = player->is_pushing;
14169   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14170   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14171   int jx = oldx, jy = oldy;
14172   int dx = x - jx, dy = y - jy;
14173   int nextx = x + dx, nexty = y + dy;
14174   int move_direction = (dx == -1 ? MV_LEFT  :
14175                         dx == +1 ? MV_RIGHT :
14176                         dy == -1 ? MV_UP    :
14177                         dy == +1 ? MV_DOWN  : MV_NONE);
14178   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14179   int dig_side = MV_DIR_OPPOSITE(move_direction);
14180   int old_element = Tile[jx][jy];
14181   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14182   int collect_count;
14183
14184   if (is_player)                // function can also be called by EL_PENGUIN
14185   {
14186     if (player->MovPos == 0)
14187     {
14188       player->is_digging = FALSE;
14189       player->is_collecting = FALSE;
14190     }
14191
14192     if (player->MovPos == 0)    // last pushing move finished
14193       player->is_pushing = FALSE;
14194
14195     if (mode == DF_NO_PUSH)     // player just stopped pushing
14196     {
14197       player->is_switching = FALSE;
14198       player->push_delay = -1;
14199
14200       return MP_NO_ACTION;
14201     }
14202   }
14203   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14204     old_element = Back[jx][jy];
14205
14206   // in case of element dropped at player position, check background
14207   else if (Back[jx][jy] != EL_EMPTY &&
14208            game.engine_version >= VERSION_IDENT(2,2,0,0))
14209     old_element = Back[jx][jy];
14210
14211   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14212     return MP_NO_ACTION;        // field has no opening in this direction
14213
14214   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element, opposite_direction))
14215     return MP_NO_ACTION;        // field has no opening in this direction
14216
14217   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14218   {
14219     SplashAcid(x, y);
14220
14221     Tile[jx][jy] = player->artwork_element;
14222     InitMovingField(jx, jy, MV_DOWN);
14223     Store[jx][jy] = EL_ACID;
14224     ContinueMoving(jx, jy);
14225     BuryPlayer(player);
14226
14227     return MP_DONT_RUN_INTO;
14228   }
14229
14230   if (player_can_move && DONT_RUN_INTO(element))
14231   {
14232     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14233
14234     return MP_DONT_RUN_INTO;
14235   }
14236
14237   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14238     return MP_NO_ACTION;
14239
14240   collect_count = element_info[element].collect_count_initial;
14241
14242   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14243     return MP_NO_ACTION;
14244
14245   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14246     player_can_move = player_can_move_or_snap;
14247
14248   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14249       game.engine_version >= VERSION_IDENT(2,2,0,0))
14250   {
14251     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14252                                player->index_bit, dig_side);
14253     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14254                                         player->index_bit, dig_side);
14255
14256     if (element == EL_DC_LANDMINE)
14257       Bang(x, y);
14258
14259     if (Tile[x][y] != element)          // field changed by snapping
14260       return MP_ACTION;
14261
14262     return MP_NO_ACTION;
14263   }
14264
14265   if (player->gravity && is_player && !player->is_auto_moving &&
14266       canFallDown(player) && move_direction != MV_DOWN &&
14267       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14268     return MP_NO_ACTION;        // player cannot walk here due to gravity
14269
14270   if (player_can_move &&
14271       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14272   {
14273     int sound_element = SND_ELEMENT(element);
14274     int sound_action = ACTION_WALKING;
14275
14276     if (IS_RND_GATE(element))
14277     {
14278       if (!player->key[RND_GATE_NR(element)])
14279         return MP_NO_ACTION;
14280     }
14281     else if (IS_RND_GATE_GRAY(element))
14282     {
14283       if (!player->key[RND_GATE_GRAY_NR(element)])
14284         return MP_NO_ACTION;
14285     }
14286     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14287     {
14288       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14289         return MP_NO_ACTION;
14290     }
14291     else if (element == EL_EXIT_OPEN ||
14292              element == EL_EM_EXIT_OPEN ||
14293              element == EL_EM_EXIT_OPENING ||
14294              element == EL_STEEL_EXIT_OPEN ||
14295              element == EL_EM_STEEL_EXIT_OPEN ||
14296              element == EL_EM_STEEL_EXIT_OPENING ||
14297              element == EL_SP_EXIT_OPEN ||
14298              element == EL_SP_EXIT_OPENING)
14299     {
14300       sound_action = ACTION_PASSING;    // player is passing exit
14301     }
14302     else if (element == EL_EMPTY)
14303     {
14304       sound_action = ACTION_MOVING;             // nothing to walk on
14305     }
14306
14307     // play sound from background or player, whatever is available
14308     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14309       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14310     else
14311       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14312   }
14313   else if (player_can_move &&
14314            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14315   {
14316     if (!ACCESS_FROM(element, opposite_direction))
14317       return MP_NO_ACTION;      // field not accessible from this direction
14318
14319     if (CAN_MOVE(element))      // only fixed elements can be passed!
14320       return MP_NO_ACTION;
14321
14322     if (IS_EM_GATE(element))
14323     {
14324       if (!player->key[EM_GATE_NR(element)])
14325         return MP_NO_ACTION;
14326     }
14327     else if (IS_EM_GATE_GRAY(element))
14328     {
14329       if (!player->key[EM_GATE_GRAY_NR(element)])
14330         return MP_NO_ACTION;
14331     }
14332     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14333     {
14334       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14335         return MP_NO_ACTION;
14336     }
14337     else if (IS_EMC_GATE(element))
14338     {
14339       if (!player->key[EMC_GATE_NR(element)])
14340         return MP_NO_ACTION;
14341     }
14342     else if (IS_EMC_GATE_GRAY(element))
14343     {
14344       if (!player->key[EMC_GATE_GRAY_NR(element)])
14345         return MP_NO_ACTION;
14346     }
14347     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14348     {
14349       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14350         return MP_NO_ACTION;
14351     }
14352     else if (element == EL_DC_GATE_WHITE ||
14353              element == EL_DC_GATE_WHITE_GRAY ||
14354              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14355     {
14356       if (player->num_white_keys == 0)
14357         return MP_NO_ACTION;
14358
14359       player->num_white_keys--;
14360     }
14361     else if (IS_SP_PORT(element))
14362     {
14363       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14364           element == EL_SP_GRAVITY_PORT_RIGHT ||
14365           element == EL_SP_GRAVITY_PORT_UP ||
14366           element == EL_SP_GRAVITY_PORT_DOWN)
14367         player->gravity = !player->gravity;
14368       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14369                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14370                element == EL_SP_GRAVITY_ON_PORT_UP ||
14371                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14372         player->gravity = TRUE;
14373       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14374                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14375                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14376                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14377         player->gravity = FALSE;
14378     }
14379
14380     // automatically move to the next field with double speed
14381     player->programmed_action = move_direction;
14382
14383     if (player->move_delay_reset_counter == 0)
14384     {
14385       player->move_delay_reset_counter = 2;     // two double speed steps
14386
14387       DOUBLE_PLAYER_SPEED(player);
14388     }
14389
14390     PlayLevelSoundAction(x, y, ACTION_PASSING);
14391   }
14392   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14393   {
14394     RemoveField(x, y);
14395
14396     if (mode != DF_SNAP)
14397     {
14398       GfxElement[x][y] = GFX_ELEMENT(element);
14399       player->is_digging = TRUE;
14400     }
14401
14402     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14403
14404     // use old behaviour for old levels (digging)
14405     if (!level.finish_dig_collect)
14406     {
14407       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14408                                           player->index_bit, dig_side);
14409
14410       // if digging triggered player relocation, finish digging tile
14411       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14412         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14413     }
14414
14415     if (mode == DF_SNAP)
14416     {
14417       if (level.block_snap_field)
14418         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14419       else
14420         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14421
14422       // use old behaviour for old levels (snapping)
14423       if (!level.finish_dig_collect)
14424         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14425                                             player->index_bit, dig_side);
14426     }
14427   }
14428   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14429   {
14430     RemoveField(x, y);
14431
14432     if (is_player && mode != DF_SNAP)
14433     {
14434       GfxElement[x][y] = element;
14435       player->is_collecting = TRUE;
14436     }
14437
14438     if (element == EL_SPEED_PILL)
14439     {
14440       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14441     }
14442     else if (element == EL_EXTRA_TIME && level.time > 0)
14443     {
14444       TimeLeft += level.extra_time;
14445
14446       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14447
14448       DisplayGameControlValues();
14449     }
14450     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14451     {
14452       int shield_time = (element == EL_SHIELD_DEADLY ?
14453                          level.shield_deadly_time :
14454                          level.shield_normal_time);
14455
14456       player->shield_normal_time_left += shield_time;
14457       if (element == EL_SHIELD_DEADLY)
14458         player->shield_deadly_time_left += shield_time;
14459     }
14460     else if (element == EL_DYNAMITE ||
14461              element == EL_EM_DYNAMITE ||
14462              element == EL_SP_DISK_RED)
14463     {
14464       if (player->inventory_size < MAX_INVENTORY_SIZE)
14465         player->inventory_element[player->inventory_size++] = element;
14466
14467       DrawGameDoorValues();
14468     }
14469     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14470     {
14471       player->dynabomb_count++;
14472       player->dynabombs_left++;
14473     }
14474     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14475     {
14476       player->dynabomb_size++;
14477     }
14478     else if (element == EL_DYNABOMB_INCREASE_POWER)
14479     {
14480       player->dynabomb_xl = TRUE;
14481     }
14482     else if (IS_KEY(element))
14483     {
14484       player->key[KEY_NR(element)] = TRUE;
14485
14486       DrawGameDoorValues();
14487     }
14488     else if (element == EL_DC_KEY_WHITE)
14489     {
14490       player->num_white_keys++;
14491
14492       // display white keys?
14493       // DrawGameDoorValues();
14494     }
14495     else if (IS_ENVELOPE(element))
14496     {
14497       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14498
14499       if (!wait_for_snapping)
14500         player->show_envelope = element;
14501     }
14502     else if (element == EL_EMC_LENSES)
14503     {
14504       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14505
14506       RedrawAllInvisibleElementsForLenses();
14507     }
14508     else if (element == EL_EMC_MAGNIFIER)
14509     {
14510       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14511
14512       RedrawAllInvisibleElementsForMagnifier();
14513     }
14514     else if (IS_DROPPABLE(element) ||
14515              IS_THROWABLE(element))     // can be collected and dropped
14516     {
14517       int i;
14518
14519       if (collect_count == 0)
14520         player->inventory_infinite_element = element;
14521       else
14522         for (i = 0; i < collect_count; i++)
14523           if (player->inventory_size < MAX_INVENTORY_SIZE)
14524             player->inventory_element[player->inventory_size++] = element;
14525
14526       DrawGameDoorValues();
14527     }
14528     else if (collect_count > 0)
14529     {
14530       game.gems_still_needed -= collect_count;
14531       if (game.gems_still_needed < 0)
14532         game.gems_still_needed = 0;
14533
14534       game.snapshot.collected_item = TRUE;
14535
14536       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14537
14538       DisplayGameControlValues();
14539     }
14540
14541     RaiseScoreElement(element);
14542     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14543
14544     // use old behaviour for old levels (collecting)
14545     if (!level.finish_dig_collect && is_player)
14546     {
14547       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14548                                           player->index_bit, dig_side);
14549
14550       // if collecting triggered player relocation, finish collecting tile
14551       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14552         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14553     }
14554
14555     if (mode == DF_SNAP)
14556     {
14557       if (level.block_snap_field)
14558         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14559       else
14560         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14561
14562       // use old behaviour for old levels (snapping)
14563       if (!level.finish_dig_collect)
14564         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14565                                             player->index_bit, dig_side);
14566     }
14567   }
14568   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14569   {
14570     if (mode == DF_SNAP && element != EL_BD_ROCK)
14571       return MP_NO_ACTION;
14572
14573     if (CAN_FALL(element) && dy)
14574       return MP_NO_ACTION;
14575
14576     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14577         !(element == EL_SPRING && level.use_spring_bug))
14578       return MP_NO_ACTION;
14579
14580     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14581         ((move_direction & MV_VERTICAL &&
14582           ((element_info[element].move_pattern & MV_LEFT &&
14583             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14584            (element_info[element].move_pattern & MV_RIGHT &&
14585             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14586          (move_direction & MV_HORIZONTAL &&
14587           ((element_info[element].move_pattern & MV_UP &&
14588             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14589            (element_info[element].move_pattern & MV_DOWN &&
14590             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14591       return MP_NO_ACTION;
14592
14593     // do not push elements already moving away faster than player
14594     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14595         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14596       return MP_NO_ACTION;
14597
14598     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14599     {
14600       if (player->push_delay_value == -1 || !player_was_pushing)
14601         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14602     }
14603     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14604     {
14605       if (player->push_delay_value == -1)
14606         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14607     }
14608     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14609     {
14610       if (!player->is_pushing)
14611         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14612     }
14613
14614     player->is_pushing = TRUE;
14615     player->is_active = TRUE;
14616
14617     if (!(IN_LEV_FIELD(nextx, nexty) &&
14618           (IS_FREE(nextx, nexty) ||
14619            (IS_SB_ELEMENT(element) &&
14620             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14621            (IS_CUSTOM_ELEMENT(element) &&
14622             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14623       return MP_NO_ACTION;
14624
14625     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14626       return MP_NO_ACTION;
14627
14628     if (player->push_delay == -1)       // new pushing; restart delay
14629       player->push_delay = 0;
14630
14631     if (player->push_delay < player->push_delay_value &&
14632         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14633         element != EL_SPRING && element != EL_BALLOON)
14634     {
14635       // make sure that there is no move delay before next try to push
14636       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14637         player->move_delay = 0;
14638
14639       return MP_NO_ACTION;
14640     }
14641
14642     if (IS_CUSTOM_ELEMENT(element) &&
14643         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14644     {
14645       if (!DigFieldByCE(nextx, nexty, element))
14646         return MP_NO_ACTION;
14647     }
14648
14649     if (IS_SB_ELEMENT(element))
14650     {
14651       boolean sokoban_task_solved = FALSE;
14652
14653       if (element == EL_SOKOBAN_FIELD_FULL)
14654       {
14655         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14656
14657         IncrementSokobanFieldsNeeded();
14658         IncrementSokobanObjectsNeeded();
14659       }
14660
14661       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14662       {
14663         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14664
14665         DecrementSokobanFieldsNeeded();
14666         DecrementSokobanObjectsNeeded();
14667
14668         // sokoban object was pushed from empty field to sokoban field
14669         if (Back[x][y] == EL_EMPTY)
14670           sokoban_task_solved = TRUE;
14671       }
14672
14673       Tile[x][y] = EL_SOKOBAN_OBJECT;
14674
14675       if (Back[x][y] == Back[nextx][nexty])
14676         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14677       else if (Back[x][y] != 0)
14678         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14679                                     ACTION_EMPTYING);
14680       else
14681         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14682                                     ACTION_FILLING);
14683
14684       if (sokoban_task_solved &&
14685           game.sokoban_fields_still_needed == 0 &&
14686           game.sokoban_objects_still_needed == 0 &&
14687           level.auto_exit_sokoban)
14688       {
14689         game.players_still_needed = 0;
14690
14691         LevelSolved();
14692
14693         PlaySound(SND_GAME_SOKOBAN_SOLVING);
14694       }
14695     }
14696     else
14697       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14698
14699     InitMovingField(x, y, move_direction);
14700     GfxAction[x][y] = ACTION_PUSHING;
14701
14702     if (mode == DF_SNAP)
14703       ContinueMoving(x, y);
14704     else
14705       MovPos[x][y] = (dx != 0 ? dx : dy);
14706
14707     Pushed[x][y] = TRUE;
14708     Pushed[nextx][nexty] = TRUE;
14709
14710     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14711       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14712     else
14713       player->push_delay_value = -1;    // get new value later
14714
14715     // check for element change _after_ element has been pushed
14716     if (game.use_change_when_pushing_bug)
14717     {
14718       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14719                                  player->index_bit, dig_side);
14720       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14721                                           player->index_bit, dig_side);
14722     }
14723   }
14724   else if (IS_SWITCHABLE(element))
14725   {
14726     if (PLAYER_SWITCHING(player, x, y))
14727     {
14728       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14729                                           player->index_bit, dig_side);
14730
14731       return MP_ACTION;
14732     }
14733
14734     player->is_switching = TRUE;
14735     player->switch_x = x;
14736     player->switch_y = y;
14737
14738     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14739
14740     if (element == EL_ROBOT_WHEEL)
14741     {
14742       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14743
14744       game.robot_wheel_x = x;
14745       game.robot_wheel_y = y;
14746       game.robot_wheel_active = TRUE;
14747
14748       TEST_DrawLevelField(x, y);
14749     }
14750     else if (element == EL_SP_TERMINAL)
14751     {
14752       int xx, yy;
14753
14754       SCAN_PLAYFIELD(xx, yy)
14755       {
14756         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14757         {
14758           Bang(xx, yy);
14759         }
14760         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14761         {
14762           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14763
14764           ResetGfxAnimation(xx, yy);
14765           TEST_DrawLevelField(xx, yy);
14766         }
14767       }
14768     }
14769     else if (IS_BELT_SWITCH(element))
14770     {
14771       ToggleBeltSwitch(x, y);
14772     }
14773     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14774              element == EL_SWITCHGATE_SWITCH_DOWN ||
14775              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14776              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14777     {
14778       ToggleSwitchgateSwitch();
14779     }
14780     else if (element == EL_LIGHT_SWITCH ||
14781              element == EL_LIGHT_SWITCH_ACTIVE)
14782     {
14783       ToggleLightSwitch(x, y);
14784     }
14785     else if (element == EL_TIMEGATE_SWITCH ||
14786              element == EL_DC_TIMEGATE_SWITCH)
14787     {
14788       ActivateTimegateSwitch(x, y);
14789     }
14790     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14791              element == EL_BALLOON_SWITCH_RIGHT ||
14792              element == EL_BALLOON_SWITCH_UP    ||
14793              element == EL_BALLOON_SWITCH_DOWN  ||
14794              element == EL_BALLOON_SWITCH_NONE  ||
14795              element == EL_BALLOON_SWITCH_ANY)
14796     {
14797       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14798                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14799                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14800                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14801                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14802                              move_direction);
14803     }
14804     else if (element == EL_LAMP)
14805     {
14806       Tile[x][y] = EL_LAMP_ACTIVE;
14807       game.lights_still_needed--;
14808
14809       ResetGfxAnimation(x, y);
14810       TEST_DrawLevelField(x, y);
14811     }
14812     else if (element == EL_TIME_ORB_FULL)
14813     {
14814       Tile[x][y] = EL_TIME_ORB_EMPTY;
14815
14816       if (level.time > 0 || level.use_time_orb_bug)
14817       {
14818         TimeLeft += level.time_orb_time;
14819         game.no_level_time_limit = FALSE;
14820
14821         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14822
14823         DisplayGameControlValues();
14824       }
14825
14826       ResetGfxAnimation(x, y);
14827       TEST_DrawLevelField(x, y);
14828     }
14829     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14830              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14831     {
14832       int xx, yy;
14833
14834       game.ball_active = !game.ball_active;
14835
14836       SCAN_PLAYFIELD(xx, yy)
14837       {
14838         int e = Tile[xx][yy];
14839
14840         if (game.ball_active)
14841         {
14842           if (e == EL_EMC_MAGIC_BALL)
14843             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14844           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14845             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14846         }
14847         else
14848         {
14849           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14850             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14851           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14852             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14853         }
14854       }
14855     }
14856
14857     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14858                                         player->index_bit, dig_side);
14859
14860     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14861                                         player->index_bit, dig_side);
14862
14863     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14864                                         player->index_bit, dig_side);
14865
14866     return MP_ACTION;
14867   }
14868   else
14869   {
14870     if (!PLAYER_SWITCHING(player, x, y))
14871     {
14872       player->is_switching = TRUE;
14873       player->switch_x = x;
14874       player->switch_y = y;
14875
14876       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14877                                  player->index_bit, dig_side);
14878       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14879                                           player->index_bit, dig_side);
14880
14881       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14882                                  player->index_bit, dig_side);
14883       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14884                                           player->index_bit, dig_side);
14885     }
14886
14887     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14888                                player->index_bit, dig_side);
14889     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14890                                         player->index_bit, dig_side);
14891
14892     return MP_NO_ACTION;
14893   }
14894
14895   player->push_delay = -1;
14896
14897   if (is_player)                // function can also be called by EL_PENGUIN
14898   {
14899     if (Tile[x][y] != element)          // really digged/collected something
14900     {
14901       player->is_collecting = !player->is_digging;
14902       player->is_active = TRUE;
14903
14904       player->last_removed_element = element;
14905     }
14906   }
14907
14908   return MP_MOVING;
14909 }
14910
14911 static boolean DigFieldByCE(int x, int y, int digging_element)
14912 {
14913   int element = Tile[x][y];
14914
14915   if (!IS_FREE(x, y))
14916   {
14917     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14918                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14919                   ACTION_BREAKING);
14920
14921     // no element can dig solid indestructible elements
14922     if (IS_INDESTRUCTIBLE(element) &&
14923         !IS_DIGGABLE(element) &&
14924         !IS_COLLECTIBLE(element))
14925       return FALSE;
14926
14927     if (AmoebaNr[x][y] &&
14928         (element == EL_AMOEBA_FULL ||
14929          element == EL_BD_AMOEBA ||
14930          element == EL_AMOEBA_GROWING))
14931     {
14932       AmoebaCnt[AmoebaNr[x][y]]--;
14933       AmoebaCnt2[AmoebaNr[x][y]]--;
14934     }
14935
14936     if (IS_MOVING(x, y))
14937       RemoveMovingField(x, y);
14938     else
14939     {
14940       RemoveField(x, y);
14941       TEST_DrawLevelField(x, y);
14942     }
14943
14944     // if digged element was about to explode, prevent the explosion
14945     ExplodeField[x][y] = EX_TYPE_NONE;
14946
14947     PlayLevelSoundAction(x, y, action);
14948   }
14949
14950   Store[x][y] = EL_EMPTY;
14951
14952   // this makes it possible to leave the removed element again
14953   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14954     Store[x][y] = element;
14955
14956   return TRUE;
14957 }
14958
14959 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14960 {
14961   int jx = player->jx, jy = player->jy;
14962   int x = jx + dx, y = jy + dy;
14963   int snap_direction = (dx == -1 ? MV_LEFT  :
14964                         dx == +1 ? MV_RIGHT :
14965                         dy == -1 ? MV_UP    :
14966                         dy == +1 ? MV_DOWN  : MV_NONE);
14967   boolean can_continue_snapping = (level.continuous_snapping &&
14968                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14969
14970   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14971     return FALSE;
14972
14973   if (!player->active || !IN_LEV_FIELD(x, y))
14974     return FALSE;
14975
14976   if (dx && dy)
14977     return FALSE;
14978
14979   if (!dx && !dy)
14980   {
14981     if (player->MovPos == 0)
14982       player->is_pushing = FALSE;
14983
14984     player->is_snapping = FALSE;
14985
14986     if (player->MovPos == 0)
14987     {
14988       player->is_moving = FALSE;
14989       player->is_digging = FALSE;
14990       player->is_collecting = FALSE;
14991     }
14992
14993     return FALSE;
14994   }
14995
14996   // prevent snapping with already pressed snap key when not allowed
14997   if (player->is_snapping && !can_continue_snapping)
14998     return FALSE;
14999
15000   player->MovDir = snap_direction;
15001
15002   if (player->MovPos == 0)
15003   {
15004     player->is_moving = FALSE;
15005     player->is_digging = FALSE;
15006     player->is_collecting = FALSE;
15007   }
15008
15009   player->is_dropping = FALSE;
15010   player->is_dropping_pressed = FALSE;
15011   player->drop_pressed_delay = 0;
15012
15013   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15014     return FALSE;
15015
15016   player->is_snapping = TRUE;
15017   player->is_active = TRUE;
15018
15019   if (player->MovPos == 0)
15020   {
15021     player->is_moving = FALSE;
15022     player->is_digging = FALSE;
15023     player->is_collecting = FALSE;
15024   }
15025
15026   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
15027     TEST_DrawLevelField(player->last_jx, player->last_jy);
15028
15029   TEST_DrawLevelField(x, y);
15030
15031   return TRUE;
15032 }
15033
15034 static boolean DropElement(struct PlayerInfo *player)
15035 {
15036   int old_element, new_element;
15037   int dropx = player->jx, dropy = player->jy;
15038   int drop_direction = player->MovDir;
15039   int drop_side = drop_direction;
15040   int drop_element = get_next_dropped_element(player);
15041
15042   /* do not drop an element on top of another element; when holding drop key
15043      pressed without moving, dropped element must move away before the next
15044      element can be dropped (this is especially important if the next element
15045      is dynamite, which can be placed on background for historical reasons) */
15046   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
15047     return MP_ACTION;
15048
15049   if (IS_THROWABLE(drop_element))
15050   {
15051     dropx += GET_DX_FROM_DIR(drop_direction);
15052     dropy += GET_DY_FROM_DIR(drop_direction);
15053
15054     if (!IN_LEV_FIELD(dropx, dropy))
15055       return FALSE;
15056   }
15057
15058   old_element = Tile[dropx][dropy];     // old element at dropping position
15059   new_element = drop_element;           // default: no change when dropping
15060
15061   // check if player is active, not moving and ready to drop
15062   if (!player->active || player->MovPos || player->drop_delay > 0)
15063     return FALSE;
15064
15065   // check if player has anything that can be dropped
15066   if (new_element == EL_UNDEFINED)
15067     return FALSE;
15068
15069   // only set if player has anything that can be dropped
15070   player->is_dropping_pressed = TRUE;
15071
15072   // check if drop key was pressed long enough for EM style dynamite
15073   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15074     return FALSE;
15075
15076   // check if anything can be dropped at the current position
15077   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15078     return FALSE;
15079
15080   // collected custom elements can only be dropped on empty fields
15081   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15082     return FALSE;
15083
15084   if (old_element != EL_EMPTY)
15085     Back[dropx][dropy] = old_element;   // store old element on this field
15086
15087   ResetGfxAnimation(dropx, dropy);
15088   ResetRandomAnimationValue(dropx, dropy);
15089
15090   if (player->inventory_size > 0 ||
15091       player->inventory_infinite_element != EL_UNDEFINED)
15092   {
15093     if (player->inventory_size > 0)
15094     {
15095       player->inventory_size--;
15096
15097       DrawGameDoorValues();
15098
15099       if (new_element == EL_DYNAMITE)
15100         new_element = EL_DYNAMITE_ACTIVE;
15101       else if (new_element == EL_EM_DYNAMITE)
15102         new_element = EL_EM_DYNAMITE_ACTIVE;
15103       else if (new_element == EL_SP_DISK_RED)
15104         new_element = EL_SP_DISK_RED_ACTIVE;
15105     }
15106
15107     Tile[dropx][dropy] = new_element;
15108
15109     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15110       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15111                           el2img(Tile[dropx][dropy]), 0);
15112
15113     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15114
15115     // needed if previous element just changed to "empty" in the last frame
15116     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15117
15118     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15119                                player->index_bit, drop_side);
15120     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15121                                         CE_PLAYER_DROPS_X,
15122                                         player->index_bit, drop_side);
15123
15124     TestIfElementTouchesCustomElement(dropx, dropy);
15125   }
15126   else          // player is dropping a dyna bomb
15127   {
15128     player->dynabombs_left--;
15129
15130     Tile[dropx][dropy] = new_element;
15131
15132     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15133       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15134                           el2img(Tile[dropx][dropy]), 0);
15135
15136     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15137   }
15138
15139   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15140     InitField_WithBug1(dropx, dropy, FALSE);
15141
15142   new_element = Tile[dropx][dropy];     // element might have changed
15143
15144   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15145       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15146   {
15147     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15148       MovDir[dropx][dropy] = drop_direction;
15149
15150     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15151
15152     // do not cause impact style collision by dropping elements that can fall
15153     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15154   }
15155
15156   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15157   player->is_dropping = TRUE;
15158
15159   player->drop_pressed_delay = 0;
15160   player->is_dropping_pressed = FALSE;
15161
15162   player->drop_x = dropx;
15163   player->drop_y = dropy;
15164
15165   return TRUE;
15166 }
15167
15168 // ----------------------------------------------------------------------------
15169 // game sound playing functions
15170 // ----------------------------------------------------------------------------
15171
15172 static int *loop_sound_frame = NULL;
15173 static int *loop_sound_volume = NULL;
15174
15175 void InitPlayLevelSound(void)
15176 {
15177   int num_sounds = getSoundListSize();
15178
15179   checked_free(loop_sound_frame);
15180   checked_free(loop_sound_volume);
15181
15182   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15183   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15184 }
15185
15186 static void PlayLevelSound(int x, int y, int nr)
15187 {
15188   int sx = SCREENX(x), sy = SCREENY(y);
15189   int volume, stereo_position;
15190   int max_distance = 8;
15191   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15192
15193   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15194       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15195     return;
15196
15197   if (!IN_LEV_FIELD(x, y) ||
15198       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15199       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15200     return;
15201
15202   volume = SOUND_MAX_VOLUME;
15203
15204   if (!IN_SCR_FIELD(sx, sy))
15205   {
15206     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15207     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15208
15209     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15210   }
15211
15212   stereo_position = (SOUND_MAX_LEFT +
15213                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15214                      (SCR_FIELDX + 2 * max_distance));
15215
15216   if (IS_LOOP_SOUND(nr))
15217   {
15218     /* This assures that quieter loop sounds do not overwrite louder ones,
15219        while restarting sound volume comparison with each new game frame. */
15220
15221     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15222       return;
15223
15224     loop_sound_volume[nr] = volume;
15225     loop_sound_frame[nr] = FrameCounter;
15226   }
15227
15228   PlaySoundExt(nr, volume, stereo_position, type);
15229 }
15230
15231 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15232 {
15233   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15234                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15235                  y < LEVELY(BY1) ? LEVELY(BY1) :
15236                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15237                  sound_action);
15238 }
15239
15240 static void PlayLevelSoundAction(int x, int y, int action)
15241 {
15242   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15243 }
15244
15245 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15246 {
15247   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15248
15249   if (sound_effect != SND_UNDEFINED)
15250     PlayLevelSound(x, y, sound_effect);
15251 }
15252
15253 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15254                                               int action)
15255 {
15256   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15257
15258   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15259     PlayLevelSound(x, y, sound_effect);
15260 }
15261
15262 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15263 {
15264   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15265
15266   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15267     PlayLevelSound(x, y, sound_effect);
15268 }
15269
15270 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15271 {
15272   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15273
15274   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15275     StopSound(sound_effect);
15276 }
15277
15278 static int getLevelMusicNr(void)
15279 {
15280   int level_pos = level_nr - leveldir_current->first_level;
15281
15282   if (levelset.music[level_nr] != MUS_UNDEFINED)
15283     return levelset.music[level_nr];            // from config file
15284   else
15285     return MAP_NOCONF_MUSIC(level_pos);         // from music dir
15286 }
15287
15288 static void FadeLevelSounds(void)
15289 {
15290   FadeSounds();
15291 }
15292
15293 static void FadeLevelMusic(void)
15294 {
15295   int music_nr = getLevelMusicNr();
15296   char *curr_music = getCurrentlyPlayingMusicFilename();
15297   char *next_music = getMusicInfoEntryFilename(music_nr);
15298
15299   if (!strEqual(curr_music, next_music))
15300     FadeMusic();
15301 }
15302
15303 void FadeLevelSoundsAndMusic(void)
15304 {
15305   FadeLevelSounds();
15306   FadeLevelMusic();
15307 }
15308
15309 static void PlayLevelMusic(void)
15310 {
15311   int music_nr = getLevelMusicNr();
15312   char *curr_music = getCurrentlyPlayingMusicFilename();
15313   char *next_music = getMusicInfoEntryFilename(music_nr);
15314
15315   if (!strEqual(curr_music, next_music))
15316     PlayMusicLoop(music_nr);
15317 }
15318
15319 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15320 {
15321   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15322   int offset = 0;
15323   int x = xx - offset;
15324   int y = yy - offset;
15325
15326   switch (sample)
15327   {
15328     case SOUND_blank:
15329       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15330       break;
15331
15332     case SOUND_roll:
15333       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15334       break;
15335
15336     case SOUND_stone:
15337       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15338       break;
15339
15340     case SOUND_nut:
15341       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15342       break;
15343
15344     case SOUND_crack:
15345       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15346       break;
15347
15348     case SOUND_bug:
15349       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15350       break;
15351
15352     case SOUND_tank:
15353       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15354       break;
15355
15356     case SOUND_android_clone:
15357       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15358       break;
15359
15360     case SOUND_android_move:
15361       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15362       break;
15363
15364     case SOUND_spring:
15365       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15366       break;
15367
15368     case SOUND_slurp:
15369       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15370       break;
15371
15372     case SOUND_eater:
15373       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15374       break;
15375
15376     case SOUND_eater_eat:
15377       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15378       break;
15379
15380     case SOUND_alien:
15381       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15382       break;
15383
15384     case SOUND_collect:
15385       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15386       break;
15387
15388     case SOUND_diamond:
15389       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15390       break;
15391
15392     case SOUND_squash:
15393       // !!! CHECK THIS !!!
15394 #if 1
15395       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15396 #else
15397       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15398 #endif
15399       break;
15400
15401     case SOUND_wonderfall:
15402       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15403       break;
15404
15405     case SOUND_drip:
15406       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15407       break;
15408
15409     case SOUND_push:
15410       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15411       break;
15412
15413     case SOUND_dirt:
15414       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15415       break;
15416
15417     case SOUND_acid:
15418       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15419       break;
15420
15421     case SOUND_ball:
15422       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15423       break;
15424
15425     case SOUND_slide:
15426       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15427       break;
15428
15429     case SOUND_wonder:
15430       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15431       break;
15432
15433     case SOUND_door:
15434       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15435       break;
15436
15437     case SOUND_exit_open:
15438       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15439       break;
15440
15441     case SOUND_exit_leave:
15442       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15443       break;
15444
15445     case SOUND_dynamite:
15446       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15447       break;
15448
15449     case SOUND_tick:
15450       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15451       break;
15452
15453     case SOUND_press:
15454       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15455       break;
15456
15457     case SOUND_wheel:
15458       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15459       break;
15460
15461     case SOUND_boom:
15462       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15463       break;
15464
15465     case SOUND_die:
15466       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15467       break;
15468
15469     case SOUND_time:
15470       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15471       break;
15472
15473     default:
15474       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15475       break;
15476   }
15477 }
15478
15479 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15480 {
15481   int element = map_element_SP_to_RND(element_sp);
15482   int action = map_action_SP_to_RND(action_sp);
15483   int offset = (setup.sp_show_border_elements ? 0 : 1);
15484   int x = xx - offset;
15485   int y = yy - offset;
15486
15487   PlayLevelSoundElementAction(x, y, element, action);
15488 }
15489
15490 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15491 {
15492   int element = map_element_MM_to_RND(element_mm);
15493   int action = map_action_MM_to_RND(action_mm);
15494   int offset = 0;
15495   int x = xx - offset;
15496   int y = yy - offset;
15497
15498   if (!IS_MM_ELEMENT(element))
15499     element = EL_MM_DEFAULT;
15500
15501   PlayLevelSoundElementAction(x, y, element, action);
15502 }
15503
15504 void PlaySound_MM(int sound_mm)
15505 {
15506   int sound = map_sound_MM_to_RND(sound_mm);
15507
15508   if (sound == SND_UNDEFINED)
15509     return;
15510
15511   PlaySound(sound);
15512 }
15513
15514 void PlaySoundLoop_MM(int sound_mm)
15515 {
15516   int sound = map_sound_MM_to_RND(sound_mm);
15517
15518   if (sound == SND_UNDEFINED)
15519     return;
15520
15521   PlaySoundLoop(sound);
15522 }
15523
15524 void StopSound_MM(int sound_mm)
15525 {
15526   int sound = map_sound_MM_to_RND(sound_mm);
15527
15528   if (sound == SND_UNDEFINED)
15529     return;
15530
15531   StopSound(sound);
15532 }
15533
15534 void RaiseScore(int value)
15535 {
15536   game.score += value;
15537
15538   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15539
15540   DisplayGameControlValues();
15541 }
15542
15543 void RaiseScoreElement(int element)
15544 {
15545   switch (element)
15546   {
15547     case EL_EMERALD:
15548     case EL_BD_DIAMOND:
15549     case EL_EMERALD_YELLOW:
15550     case EL_EMERALD_RED:
15551     case EL_EMERALD_PURPLE:
15552     case EL_SP_INFOTRON:
15553       RaiseScore(level.score[SC_EMERALD]);
15554       break;
15555     case EL_DIAMOND:
15556       RaiseScore(level.score[SC_DIAMOND]);
15557       break;
15558     case EL_CRYSTAL:
15559       RaiseScore(level.score[SC_CRYSTAL]);
15560       break;
15561     case EL_PEARL:
15562       RaiseScore(level.score[SC_PEARL]);
15563       break;
15564     case EL_BUG:
15565     case EL_BD_BUTTERFLY:
15566     case EL_SP_ELECTRON:
15567       RaiseScore(level.score[SC_BUG]);
15568       break;
15569     case EL_SPACESHIP:
15570     case EL_BD_FIREFLY:
15571     case EL_SP_SNIKSNAK:
15572       RaiseScore(level.score[SC_SPACESHIP]);
15573       break;
15574     case EL_YAMYAM:
15575     case EL_DARK_YAMYAM:
15576       RaiseScore(level.score[SC_YAMYAM]);
15577       break;
15578     case EL_ROBOT:
15579       RaiseScore(level.score[SC_ROBOT]);
15580       break;
15581     case EL_PACMAN:
15582       RaiseScore(level.score[SC_PACMAN]);
15583       break;
15584     case EL_NUT:
15585       RaiseScore(level.score[SC_NUT]);
15586       break;
15587     case EL_DYNAMITE:
15588     case EL_EM_DYNAMITE:
15589     case EL_SP_DISK_RED:
15590     case EL_DYNABOMB_INCREASE_NUMBER:
15591     case EL_DYNABOMB_INCREASE_SIZE:
15592     case EL_DYNABOMB_INCREASE_POWER:
15593       RaiseScore(level.score[SC_DYNAMITE]);
15594       break;
15595     case EL_SHIELD_NORMAL:
15596     case EL_SHIELD_DEADLY:
15597       RaiseScore(level.score[SC_SHIELD]);
15598       break;
15599     case EL_EXTRA_TIME:
15600       RaiseScore(level.extra_time_score);
15601       break;
15602     case EL_KEY_1:
15603     case EL_KEY_2:
15604     case EL_KEY_3:
15605     case EL_KEY_4:
15606     case EL_EM_KEY_1:
15607     case EL_EM_KEY_2:
15608     case EL_EM_KEY_3:
15609     case EL_EM_KEY_4:
15610     case EL_EMC_KEY_5:
15611     case EL_EMC_KEY_6:
15612     case EL_EMC_KEY_7:
15613     case EL_EMC_KEY_8:
15614     case EL_DC_KEY_WHITE:
15615       RaiseScore(level.score[SC_KEY]);
15616       break;
15617     default:
15618       RaiseScore(element_info[element].collect_score);
15619       break;
15620   }
15621 }
15622
15623 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15624 {
15625   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15626   {
15627     if (!quick_quit)
15628     {
15629       // prevent short reactivation of overlay buttons while closing door
15630       SetOverlayActive(FALSE);
15631       UnmapGameButtons();
15632
15633       // door may still be open due to skipped or envelope style request
15634       CloseDoor(score_info_tape_play ? DOOR_CLOSE_ALL : DOOR_CLOSE_1);
15635     }
15636
15637     if (network.enabled)
15638       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15639     else
15640     {
15641       if (quick_quit)
15642         FadeSkipNextFadeIn();
15643
15644       SetGameStatus(GAME_MODE_MAIN);
15645
15646       DrawMainMenu();
15647     }
15648   }
15649   else          // continue playing the game
15650   {
15651     if (tape.playing && tape.deactivate_display)
15652       TapeDeactivateDisplayOff(TRUE);
15653
15654     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15655
15656     if (tape.playing && tape.deactivate_display)
15657       TapeDeactivateDisplayOn();
15658   }
15659 }
15660
15661 void RequestQuitGame(boolean escape_key_pressed)
15662 {
15663   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15664   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15665                         level_editor_test_game);
15666   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15667                           quick_quit || score_info_tape_play);
15668
15669   RequestQuitGameExt(skip_request, quick_quit,
15670                      "Do you really want to quit the game?");
15671 }
15672
15673 static char *getRestartGameMessage(void)
15674 {
15675   boolean play_again = hasStartedNetworkGame();
15676   static char message[MAX_OUTPUT_LINESIZE];
15677   char *game_over_text = "Game over!";
15678   char *play_again_text = " Play it again?";
15679
15680   if (level.game_engine_type == GAME_ENGINE_TYPE_MM &&
15681       game_mm.game_over_message != NULL)
15682     game_over_text = game_mm.game_over_message;
15683
15684   snprintf(message, MAX_OUTPUT_LINESIZE, "%s%s", game_over_text,
15685            (play_again ? play_again_text : ""));
15686
15687   return message;
15688 }
15689
15690 static void RequestRestartGame(void)
15691 {
15692   char *message = getRestartGameMessage();
15693   boolean has_started_game = hasStartedNetworkGame();
15694   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15695   int door_state = DOOR_CLOSE_1;
15696
15697   if (Request(message, request_mode | REQ_STAY_OPEN) && has_started_game)
15698   {
15699     CloseDoor(door_state);
15700
15701     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15702   }
15703   else
15704   {
15705     // if game was invoked from level editor, also close tape recorder door
15706     if (level_editor_test_game)
15707       door_state = DOOR_CLOSE_ALL;
15708
15709     CloseDoor(door_state);
15710
15711     SetGameStatus(GAME_MODE_MAIN);
15712
15713     DrawMainMenu();
15714   }
15715 }
15716
15717 boolean CheckRestartGame(void)
15718 {
15719   static int game_over_delay = 0;
15720   int game_over_delay_value = 50;
15721   boolean game_over = checkGameFailed();
15722
15723   if (!game_over)
15724   {
15725     game_over_delay = game_over_delay_value;
15726
15727     return FALSE;
15728   }
15729
15730   if (game_over_delay > 0)
15731   {
15732     if (game_over_delay == game_over_delay_value / 2)
15733       PlaySound(SND_GAME_LOSING);
15734
15735     game_over_delay--;
15736
15737     return FALSE;
15738   }
15739
15740   // do not handle game over if request dialog is already active
15741   if (game.request_active)
15742     return FALSE;
15743
15744   // do not ask to play again if game was never actually played
15745   if (!game.GamePlayed)
15746     return FALSE;
15747
15748   // do not ask to play again if this was disabled in setup menu
15749   if (!setup.ask_on_game_over)
15750     return FALSE;
15751
15752   RequestRestartGame();
15753
15754   return TRUE;
15755 }
15756
15757 boolean checkGameSolved(void)
15758 {
15759   // set for all game engines if level was solved
15760   return game.LevelSolved_GameEnd;
15761 }
15762
15763 boolean checkGameFailed(void)
15764 {
15765   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15766     return (game_em.game_over && !game_em.level_solved);
15767   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15768     return (game_sp.game_over && !game_sp.level_solved);
15769   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15770     return (game_mm.game_over && !game_mm.level_solved);
15771   else                          // GAME_ENGINE_TYPE_RND
15772     return (game.GameOver && !game.LevelSolved);
15773 }
15774
15775 boolean checkGameEnded(void)
15776 {
15777   return (checkGameSolved() || checkGameFailed());
15778 }
15779
15780
15781 // ----------------------------------------------------------------------------
15782 // random generator functions
15783 // ----------------------------------------------------------------------------
15784
15785 unsigned int InitEngineRandom_RND(int seed)
15786 {
15787   game.num_random_calls = 0;
15788
15789   return InitEngineRandom(seed);
15790 }
15791
15792 unsigned int RND(int max)
15793 {
15794   if (max > 0)
15795   {
15796     game.num_random_calls++;
15797
15798     return GetEngineRandom(max);
15799   }
15800
15801   return 0;
15802 }
15803
15804
15805 // ----------------------------------------------------------------------------
15806 // game engine snapshot handling functions
15807 // ----------------------------------------------------------------------------
15808
15809 struct EngineSnapshotInfo
15810 {
15811   // runtime values for custom element collect score
15812   int collect_score[NUM_CUSTOM_ELEMENTS];
15813
15814   // runtime values for group element choice position
15815   int choice_pos[NUM_GROUP_ELEMENTS];
15816
15817   // runtime values for belt position animations
15818   int belt_graphic[4][NUM_BELT_PARTS];
15819   int belt_anim_mode[4][NUM_BELT_PARTS];
15820 };
15821
15822 static struct EngineSnapshotInfo engine_snapshot_rnd;
15823 static char *snapshot_level_identifier = NULL;
15824 static int snapshot_level_nr = -1;
15825
15826 static void SaveEngineSnapshotValues_RND(void)
15827 {
15828   static int belt_base_active_element[4] =
15829   {
15830     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15831     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15832     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15833     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15834   };
15835   int i, j;
15836
15837   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15838   {
15839     int element = EL_CUSTOM_START + i;
15840
15841     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15842   }
15843
15844   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15845   {
15846     int element = EL_GROUP_START + i;
15847
15848     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15849   }
15850
15851   for (i = 0; i < 4; i++)
15852   {
15853     for (j = 0; j < NUM_BELT_PARTS; j++)
15854     {
15855       int element = belt_base_active_element[i] + j;
15856       int graphic = el2img(element);
15857       int anim_mode = graphic_info[graphic].anim_mode;
15858
15859       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15860       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15861     }
15862   }
15863 }
15864
15865 static void LoadEngineSnapshotValues_RND(void)
15866 {
15867   unsigned int num_random_calls = game.num_random_calls;
15868   int i, j;
15869
15870   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15871   {
15872     int element = EL_CUSTOM_START + i;
15873
15874     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15875   }
15876
15877   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15878   {
15879     int element = EL_GROUP_START + i;
15880
15881     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15882   }
15883
15884   for (i = 0; i < 4; i++)
15885   {
15886     for (j = 0; j < NUM_BELT_PARTS; j++)
15887     {
15888       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15889       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15890
15891       graphic_info[graphic].anim_mode = anim_mode;
15892     }
15893   }
15894
15895   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15896   {
15897     InitRND(tape.random_seed);
15898     for (i = 0; i < num_random_calls; i++)
15899       RND(1);
15900   }
15901
15902   if (game.num_random_calls != num_random_calls)
15903   {
15904     Error("number of random calls out of sync");
15905     Error("number of random calls should be %d", num_random_calls);
15906     Error("number of random calls is %d", game.num_random_calls);
15907
15908     Fail("this should not happen -- please debug");
15909   }
15910 }
15911
15912 void FreeEngineSnapshotSingle(void)
15913 {
15914   FreeSnapshotSingle();
15915
15916   setString(&snapshot_level_identifier, NULL);
15917   snapshot_level_nr = -1;
15918 }
15919
15920 void FreeEngineSnapshotList(void)
15921 {
15922   FreeSnapshotList();
15923 }
15924
15925 static ListNode *SaveEngineSnapshotBuffers(void)
15926 {
15927   ListNode *buffers = NULL;
15928
15929   // copy some special values to a structure better suited for the snapshot
15930
15931   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15932     SaveEngineSnapshotValues_RND();
15933   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15934     SaveEngineSnapshotValues_EM();
15935   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15936     SaveEngineSnapshotValues_SP(&buffers);
15937   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15938     SaveEngineSnapshotValues_MM();
15939
15940   // save values stored in special snapshot structure
15941
15942   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15943     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15944   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15945     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15946   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15947     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15948   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15949     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15950
15951   // save further RND engine values
15952
15953   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15954   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15955   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15956
15957   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15958   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15959   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15960   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15961   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15962
15963   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15964   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15965   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15966
15967   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15968
15969   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15970   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15971
15972   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15973   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15974   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15975   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15976   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15977   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15978   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15979   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15980   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15981   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15982   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15983   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15984   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15985   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15986   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15987   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15988   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15989   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15990
15991   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15992   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15993
15994   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15995   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15996   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15997
15998   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15999   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16000
16001   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16002   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16003   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
16004   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16005   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16006   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16007
16008   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16009   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16010
16011 #if 0
16012   ListNode *node = engine_snapshot_list_rnd;
16013   int num_bytes = 0;
16014
16015   while (node != NULL)
16016   {
16017     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16018
16019     node = node->next;
16020   }
16021
16022   Debug("game:playing:SaveEngineSnapshotBuffers",
16023         "size of engine snapshot: %d bytes", num_bytes);
16024 #endif
16025
16026   return buffers;
16027 }
16028
16029 void SaveEngineSnapshotSingle(void)
16030 {
16031   ListNode *buffers = SaveEngineSnapshotBuffers();
16032
16033   // finally save all snapshot buffers to single snapshot
16034   SaveSnapshotSingle(buffers);
16035
16036   // save level identification information
16037   setString(&snapshot_level_identifier, leveldir_current->identifier);
16038   snapshot_level_nr = level_nr;
16039 }
16040
16041 boolean CheckSaveEngineSnapshotToList(void)
16042 {
16043   boolean save_snapshot =
16044     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
16045      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
16046       game.snapshot.changed_action) ||
16047      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16048       game.snapshot.collected_item));
16049
16050   game.snapshot.changed_action = FALSE;
16051   game.snapshot.collected_item = FALSE;
16052   game.snapshot.save_snapshot = save_snapshot;
16053
16054   return save_snapshot;
16055 }
16056
16057 void SaveEngineSnapshotToList(void)
16058 {
16059   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
16060       tape.quick_resume)
16061     return;
16062
16063   ListNode *buffers = SaveEngineSnapshotBuffers();
16064
16065   // finally save all snapshot buffers to snapshot list
16066   SaveSnapshotToList(buffers);
16067 }
16068
16069 void SaveEngineSnapshotToListInitial(void)
16070 {
16071   FreeEngineSnapshotList();
16072
16073   SaveEngineSnapshotToList();
16074 }
16075
16076 static void LoadEngineSnapshotValues(void)
16077 {
16078   // restore special values from snapshot structure
16079
16080   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16081     LoadEngineSnapshotValues_RND();
16082   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16083     LoadEngineSnapshotValues_EM();
16084   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16085     LoadEngineSnapshotValues_SP();
16086   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16087     LoadEngineSnapshotValues_MM();
16088 }
16089
16090 void LoadEngineSnapshotSingle(void)
16091 {
16092   LoadSnapshotSingle();
16093
16094   LoadEngineSnapshotValues();
16095 }
16096
16097 static void LoadEngineSnapshot_Undo(int steps)
16098 {
16099   LoadSnapshotFromList_Older(steps);
16100
16101   LoadEngineSnapshotValues();
16102 }
16103
16104 static void LoadEngineSnapshot_Redo(int steps)
16105 {
16106   LoadSnapshotFromList_Newer(steps);
16107
16108   LoadEngineSnapshotValues();
16109 }
16110
16111 boolean CheckEngineSnapshotSingle(void)
16112 {
16113   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16114           snapshot_level_nr == level_nr);
16115 }
16116
16117 boolean CheckEngineSnapshotList(void)
16118 {
16119   return CheckSnapshotList();
16120 }
16121
16122
16123 // ---------- new game button stuff -------------------------------------------
16124
16125 static struct
16126 {
16127   int graphic;
16128   struct XY *pos;
16129   int gadget_id;
16130   boolean *setup_value;
16131   boolean allowed_on_tape;
16132   boolean is_touch_button;
16133   char *infotext;
16134 } gamebutton_info[NUM_GAME_BUTTONS] =
16135 {
16136   {
16137     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16138     GAME_CTRL_ID_STOP,                          NULL,
16139     TRUE, FALSE,                                "stop game"
16140   },
16141   {
16142     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16143     GAME_CTRL_ID_PAUSE,                         NULL,
16144     TRUE, FALSE,                                "pause game"
16145   },
16146   {
16147     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16148     GAME_CTRL_ID_PLAY,                          NULL,
16149     TRUE, FALSE,                                "play game"
16150   },
16151   {
16152     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16153     GAME_CTRL_ID_UNDO,                          NULL,
16154     TRUE, FALSE,                                "undo step"
16155   },
16156   {
16157     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16158     GAME_CTRL_ID_REDO,                          NULL,
16159     TRUE, FALSE,                                "redo step"
16160   },
16161   {
16162     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16163     GAME_CTRL_ID_SAVE,                          NULL,
16164     TRUE, FALSE,                                "save game"
16165   },
16166   {
16167     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16168     GAME_CTRL_ID_PAUSE2,                        NULL,
16169     TRUE, FALSE,                                "pause game"
16170   },
16171   {
16172     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16173     GAME_CTRL_ID_LOAD,                          NULL,
16174     TRUE, FALSE,                                "load game"
16175   },
16176   {
16177     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16178     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16179     FALSE, FALSE,                               "stop game"
16180   },
16181   {
16182     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16183     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16184     FALSE, FALSE,                               "pause game"
16185   },
16186   {
16187     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16188     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16189     FALSE, FALSE,                               "play game"
16190   },
16191   {
16192     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16193     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16194     FALSE, TRUE,                                "stop game"
16195   },
16196   {
16197     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16198     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16199     FALSE, TRUE,                                "pause game"
16200   },
16201   {
16202     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16203     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16204     TRUE, FALSE,                                "background music on/off"
16205   },
16206   {
16207     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16208     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16209     TRUE, FALSE,                                "sound loops on/off"
16210   },
16211   {
16212     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16213     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16214     TRUE, FALSE,                                "normal sounds on/off"
16215   },
16216   {
16217     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16218     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16219     FALSE, FALSE,                               "background music on/off"
16220   },
16221   {
16222     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16223     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16224     FALSE, FALSE,                               "sound loops on/off"
16225   },
16226   {
16227     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16228     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16229     FALSE, FALSE,                               "normal sounds on/off"
16230   }
16231 };
16232
16233 void CreateGameButtons(void)
16234 {
16235   int i;
16236
16237   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16238   {
16239     int graphic = gamebutton_info[i].graphic;
16240     struct GraphicInfo *gfx = &graphic_info[graphic];
16241     struct XY *pos = gamebutton_info[i].pos;
16242     struct GadgetInfo *gi;
16243     int button_type;
16244     boolean checked;
16245     unsigned int event_mask;
16246     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16247     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16248     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16249     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16250     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16251     int gd_x   = gfx->src_x;
16252     int gd_y   = gfx->src_y;
16253     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16254     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16255     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16256     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16257     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16258     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16259     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16260     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16261     int id = i;
16262
16263     // do not use touch buttons if overlay touch buttons are disabled
16264     if (is_touch_button && !setup.touch.overlay_buttons)
16265       continue;
16266
16267     if (gfx->bitmap == NULL)
16268     {
16269       game_gadget[id] = NULL;
16270
16271       continue;
16272     }
16273
16274     if (id == GAME_CTRL_ID_STOP ||
16275         id == GAME_CTRL_ID_PANEL_STOP ||
16276         id == GAME_CTRL_ID_TOUCH_STOP ||
16277         id == GAME_CTRL_ID_PLAY ||
16278         id == GAME_CTRL_ID_PANEL_PLAY ||
16279         id == GAME_CTRL_ID_SAVE ||
16280         id == GAME_CTRL_ID_LOAD)
16281     {
16282       button_type = GD_TYPE_NORMAL_BUTTON;
16283       checked = FALSE;
16284       event_mask = GD_EVENT_RELEASED;
16285     }
16286     else if (id == GAME_CTRL_ID_UNDO ||
16287              id == GAME_CTRL_ID_REDO)
16288     {
16289       button_type = GD_TYPE_NORMAL_BUTTON;
16290       checked = FALSE;
16291       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16292     }
16293     else
16294     {
16295       button_type = GD_TYPE_CHECK_BUTTON;
16296       checked = (gamebutton_info[i].setup_value != NULL ?
16297                  *gamebutton_info[i].setup_value : FALSE);
16298       event_mask = GD_EVENT_PRESSED;
16299     }
16300
16301     gi = CreateGadget(GDI_CUSTOM_ID, id,
16302                       GDI_IMAGE_ID, graphic,
16303                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16304                       GDI_X, base_x + x,
16305                       GDI_Y, base_y + y,
16306                       GDI_WIDTH, gfx->width,
16307                       GDI_HEIGHT, gfx->height,
16308                       GDI_TYPE, button_type,
16309                       GDI_STATE, GD_BUTTON_UNPRESSED,
16310                       GDI_CHECKED, checked,
16311                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16312                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16313                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16314                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16315                       GDI_DIRECT_DRAW, FALSE,
16316                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16317                       GDI_EVENT_MASK, event_mask,
16318                       GDI_CALLBACK_ACTION, HandleGameButtons,
16319                       GDI_END);
16320
16321     if (gi == NULL)
16322       Fail("cannot create gadget");
16323
16324     game_gadget[id] = gi;
16325   }
16326 }
16327
16328 void FreeGameButtons(void)
16329 {
16330   int i;
16331
16332   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16333     FreeGadget(game_gadget[i]);
16334 }
16335
16336 static void UnmapGameButtonsAtSamePosition(int id)
16337 {
16338   int i;
16339
16340   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16341     if (i != id &&
16342         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16343         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16344       UnmapGadget(game_gadget[i]);
16345 }
16346
16347 static void UnmapGameButtonsAtSamePosition_All(void)
16348 {
16349   if (setup.show_load_save_buttons)
16350   {
16351     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16352     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16353     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16354   }
16355   else if (setup.show_undo_redo_buttons)
16356   {
16357     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16358     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16359     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16360   }
16361   else
16362   {
16363     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16364     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16365     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16366
16367     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16368     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16369     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16370   }
16371 }
16372
16373 void MapLoadSaveButtons(void)
16374 {
16375   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16376   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16377
16378   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
16379   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
16380 }
16381
16382 void MapUndoRedoButtons(void)
16383 {
16384   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16385   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16386
16387   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16388   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16389 }
16390
16391 void ModifyPauseButtons(void)
16392 {
16393   static int ids[] =
16394   {
16395     GAME_CTRL_ID_PAUSE,
16396     GAME_CTRL_ID_PAUSE2,
16397     GAME_CTRL_ID_PANEL_PAUSE,
16398     GAME_CTRL_ID_TOUCH_PAUSE,
16399     -1
16400   };
16401   int i;
16402
16403   for (i = 0; ids[i] > -1; i++)
16404     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16405 }
16406
16407 static void MapGameButtonsExt(boolean on_tape)
16408 {
16409   int i;
16410
16411   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16412   {
16413     if ((i == GAME_CTRL_ID_UNDO ||
16414          i == GAME_CTRL_ID_REDO) &&
16415         game_status != GAME_MODE_PLAYING)
16416       continue;
16417
16418     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16419       MapGadget(game_gadget[i]);
16420   }
16421
16422   UnmapGameButtonsAtSamePosition_All();
16423
16424   RedrawGameButtons();
16425 }
16426
16427 static void UnmapGameButtonsExt(boolean on_tape)
16428 {
16429   int i;
16430
16431   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16432     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16433       UnmapGadget(game_gadget[i]);
16434 }
16435
16436 static void RedrawGameButtonsExt(boolean on_tape)
16437 {
16438   int i;
16439
16440   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16441     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16442       RedrawGadget(game_gadget[i]);
16443 }
16444
16445 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16446 {
16447   if (gi == NULL)
16448     return;
16449
16450   gi->checked = state;
16451 }
16452
16453 static void RedrawSoundButtonGadget(int id)
16454 {
16455   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16456              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16457              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16458              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16459              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16460              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16461              id);
16462
16463   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16464   RedrawGadget(game_gadget[id2]);
16465 }
16466
16467 void MapGameButtons(void)
16468 {
16469   MapGameButtonsExt(FALSE);
16470 }
16471
16472 void UnmapGameButtons(void)
16473 {
16474   UnmapGameButtonsExt(FALSE);
16475 }
16476
16477 void RedrawGameButtons(void)
16478 {
16479   RedrawGameButtonsExt(FALSE);
16480 }
16481
16482 void MapGameButtonsOnTape(void)
16483 {
16484   MapGameButtonsExt(TRUE);
16485 }
16486
16487 void UnmapGameButtonsOnTape(void)
16488 {
16489   UnmapGameButtonsExt(TRUE);
16490 }
16491
16492 void RedrawGameButtonsOnTape(void)
16493 {
16494   RedrawGameButtonsExt(TRUE);
16495 }
16496
16497 static void GameUndoRedoExt(void)
16498 {
16499   ClearPlayerAction();
16500
16501   tape.pausing = TRUE;
16502
16503   RedrawPlayfield();
16504   UpdateAndDisplayGameControlValues();
16505
16506   DrawCompleteVideoDisplay();
16507   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16508   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16509   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16510
16511   ModifyPauseButtons();
16512
16513   BackToFront();
16514 }
16515
16516 static void GameUndo(int steps)
16517 {
16518   if (!CheckEngineSnapshotList())
16519     return;
16520
16521   int tape_property_bits = tape.property_bits;
16522
16523   LoadEngineSnapshot_Undo(steps);
16524
16525   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16526
16527   GameUndoRedoExt();
16528 }
16529
16530 static void GameRedo(int steps)
16531 {
16532   if (!CheckEngineSnapshotList())
16533     return;
16534
16535   int tape_property_bits = tape.property_bits;
16536
16537   LoadEngineSnapshot_Redo(steps);
16538
16539   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16540
16541   GameUndoRedoExt();
16542 }
16543
16544 static void HandleGameButtonsExt(int id, int button)
16545 {
16546   static boolean game_undo_executed = FALSE;
16547   int steps = BUTTON_STEPSIZE(button);
16548   boolean handle_game_buttons =
16549     (game_status == GAME_MODE_PLAYING ||
16550      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16551
16552   if (!handle_game_buttons)
16553     return;
16554
16555   switch (id)
16556   {
16557     case GAME_CTRL_ID_STOP:
16558     case GAME_CTRL_ID_PANEL_STOP:
16559     case GAME_CTRL_ID_TOUCH_STOP:
16560       TapeStopGame();
16561
16562       break;
16563
16564     case GAME_CTRL_ID_PAUSE:
16565     case GAME_CTRL_ID_PAUSE2:
16566     case GAME_CTRL_ID_PANEL_PAUSE:
16567     case GAME_CTRL_ID_TOUCH_PAUSE:
16568       if (network.enabled && game_status == GAME_MODE_PLAYING)
16569       {
16570         if (tape.pausing)
16571           SendToServer_ContinuePlaying();
16572         else
16573           SendToServer_PausePlaying();
16574       }
16575       else
16576         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16577
16578       game_undo_executed = FALSE;
16579
16580       break;
16581
16582     case GAME_CTRL_ID_PLAY:
16583     case GAME_CTRL_ID_PANEL_PLAY:
16584       if (game_status == GAME_MODE_MAIN)
16585       {
16586         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16587       }
16588       else if (tape.pausing)
16589       {
16590         if (network.enabled)
16591           SendToServer_ContinuePlaying();
16592         else
16593           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16594       }
16595       break;
16596
16597     case GAME_CTRL_ID_UNDO:
16598       // Important: When using "save snapshot when collecting an item" mode,
16599       // load last (current) snapshot for first "undo" after pressing "pause"
16600       // (else the last-but-one snapshot would be loaded, because the snapshot
16601       // pointer already points to the last snapshot when pressing "pause",
16602       // which is fine for "every step/move" mode, but not for "every collect")
16603       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16604           !game_undo_executed)
16605         steps--;
16606
16607       game_undo_executed = TRUE;
16608
16609       GameUndo(steps);
16610       break;
16611
16612     case GAME_CTRL_ID_REDO:
16613       GameRedo(steps);
16614       break;
16615
16616     case GAME_CTRL_ID_SAVE:
16617       TapeQuickSave();
16618       break;
16619
16620     case GAME_CTRL_ID_LOAD:
16621       TapeQuickLoad();
16622       break;
16623
16624     case SOUND_CTRL_ID_MUSIC:
16625     case SOUND_CTRL_ID_PANEL_MUSIC:
16626       if (setup.sound_music)
16627       { 
16628         setup.sound_music = FALSE;
16629
16630         FadeMusic();
16631       }
16632       else if (audio.music_available)
16633       { 
16634         setup.sound = setup.sound_music = TRUE;
16635
16636         SetAudioMode(setup.sound);
16637
16638         if (game_status == GAME_MODE_PLAYING)
16639           PlayLevelMusic();
16640       }
16641
16642       RedrawSoundButtonGadget(id);
16643
16644       break;
16645
16646     case SOUND_CTRL_ID_LOOPS:
16647     case SOUND_CTRL_ID_PANEL_LOOPS:
16648       if (setup.sound_loops)
16649         setup.sound_loops = FALSE;
16650       else if (audio.loops_available)
16651       {
16652         setup.sound = setup.sound_loops = TRUE;
16653
16654         SetAudioMode(setup.sound);
16655       }
16656
16657       RedrawSoundButtonGadget(id);
16658
16659       break;
16660
16661     case SOUND_CTRL_ID_SIMPLE:
16662     case SOUND_CTRL_ID_PANEL_SIMPLE:
16663       if (setup.sound_simple)
16664         setup.sound_simple = FALSE;
16665       else if (audio.sound_available)
16666       {
16667         setup.sound = setup.sound_simple = TRUE;
16668
16669         SetAudioMode(setup.sound);
16670       }
16671
16672       RedrawSoundButtonGadget(id);
16673
16674       break;
16675
16676     default:
16677       break;
16678   }
16679 }
16680
16681 static void HandleGameButtons(struct GadgetInfo *gi)
16682 {
16683   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16684 }
16685
16686 void HandleSoundButtonKeys(Key key)
16687 {
16688   if (key == setup.shortcut.sound_simple)
16689     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16690   else if (key == setup.shortcut.sound_loops)
16691     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16692   else if (key == setup.shortcut.sound_music)
16693     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16694 }