aa047870511da6ff1f499990c602d17ef5ae8d0e
[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_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
883                                  RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
886                                  RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
888                                  RND((c)->delay_random))
889
890
891 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
892          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
893
894 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
895         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
896          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
897          (be) + (e) - EL_SELF)
898
899 #define GET_PLAYER_FROM_BITS(p)                                         \
900         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
901
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
903         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
904          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
905          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
906          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
907          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
908          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
909          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
910          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
911          (e))
912
913 #define CAN_GROW_INTO(e)                                                \
914         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
915
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
917                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
918                                         (condition)))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (CAN_MOVE_INTO_ACID(e) &&       \
923                                          Tile[x][y] == EL_ACID) ||      \
924                                         (condition)))
925
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
927                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
928                                         (CAN_MOVE_INTO_ACID(e) &&       \
929                                          Tile[x][y] == EL_ACID) ||      \
930                                         (condition)))
931
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
933                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
934                                         (condition) ||                  \
935                                         (CAN_MOVE_INTO_ACID(e) &&       \
936                                          Tile[x][y] == EL_ACID) ||      \
937                                         (DONT_COLLIDE_WITH(e) &&        \
938                                          IS_PLAYER(x, y) &&             \
939                                          !PLAYER_ENEMY_PROTECTED(x, y))))
940
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
942         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
943
944 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
945         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
946
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
948         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Tile[x][y] == EL_EMC_PLANT)
949
950 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
951         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Tile[x][y]) || \
952                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
953
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
955         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
956
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
958         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Tile[x][y] == EL_DIAMOND)
959
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
961         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Tile[x][y]))
962
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
964         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Tile[x][y]))
965
966 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
967         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Tile[x][y]))
968
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
970         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Tile[x][y] == EL_EXIT_OPEN || \
971                                                  Tile[x][y] == EL_EM_EXIT_OPEN || \
972                                                  Tile[x][y] == EL_STEEL_EXIT_OPEN || \
973                                                  Tile[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974                                                  IS_FOOD_PENGUIN(Tile[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
976         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
977
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
979         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
980
981 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
982         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
983
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
985         (IN_LEV_FIELD(x, y) && (Tile[x][y] == EL_EMC_SPRING_BUMPER ||   \
986                                 Tile[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
987
988 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
989
990 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
991                 (!IS_PLAYER(x, y) &&                                    \
992                  IS_EQUAL_OR_IN_GROUP(Tile[x][y], MOVE_ENTER_EL(e)))
993
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
995         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
996
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
999
1000 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1004
1005 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1006
1007 // game button identifiers
1008 #define GAME_CTRL_ID_STOP               0
1009 #define GAME_CTRL_ID_PAUSE              1
1010 #define GAME_CTRL_ID_PLAY               2
1011 #define GAME_CTRL_ID_UNDO               3
1012 #define GAME_CTRL_ID_REDO               4
1013 #define GAME_CTRL_ID_SAVE               5
1014 #define GAME_CTRL_ID_PAUSE2             6
1015 #define GAME_CTRL_ID_LOAD               7
1016 #define GAME_CTRL_ID_PANEL_STOP         8
1017 #define GAME_CTRL_ID_PANEL_PAUSE        9
1018 #define GAME_CTRL_ID_PANEL_PLAY         10
1019 #define GAME_CTRL_ID_TOUCH_STOP         11
1020 #define GAME_CTRL_ID_TOUCH_PAUSE        12
1021 #define SOUND_CTRL_ID_MUSIC             13
1022 #define SOUND_CTRL_ID_LOOPS             14
1023 #define SOUND_CTRL_ID_SIMPLE            15
1024 #define SOUND_CTRL_ID_PANEL_MUSIC       16
1025 #define SOUND_CTRL_ID_PANEL_LOOPS       17
1026 #define SOUND_CTRL_ID_PANEL_SIMPLE      18
1027
1028 #define NUM_GAME_BUTTONS                19
1029
1030
1031 // forward declaration for internal use
1032
1033 static void CreateField(int, int, int);
1034
1035 static void ResetGfxAnimation(int, int);
1036
1037 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1038 static void AdvanceFrameAndPlayerCounters(int);
1039
1040 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1041 static boolean MovePlayer(struct PlayerInfo *, int, int);
1042 static void ScrollPlayer(struct PlayerInfo *, int);
1043 static void ScrollScreen(struct PlayerInfo *, int);
1044
1045 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1046 static boolean DigFieldByCE(int, int, int);
1047 static boolean SnapField(struct PlayerInfo *, int, int);
1048 static boolean DropElement(struct PlayerInfo *);
1049
1050 static void InitBeltMovement(void);
1051 static void CloseAllOpenTimegates(void);
1052 static void CheckGravityMovement(struct PlayerInfo *);
1053 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1054 static void KillPlayerUnlessEnemyProtected(int, int);
1055 static void KillPlayerUnlessExplosionProtected(int, int);
1056
1057 static void TestIfPlayerTouchesCustomElement(int, int);
1058 static void TestIfElementTouchesCustomElement(int, int);
1059 static void TestIfElementHitsCustomElement(int, int, int);
1060
1061 static void HandleElementChange(int, int, int);
1062 static void ExecuteCustomElementAction(int, int, int, int);
1063 static boolean ChangeElement(int, int, int, int);
1064
1065 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1066 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1067         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1068 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1069         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1070 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1071         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1072 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1073         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1074
1075 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1076 #define CheckElementChange(x, y, e, te, ev)                             \
1077         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1078 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1079         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1080 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1081         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1082
1083 static void PlayLevelSound(int, int, int);
1084 static void PlayLevelSoundNearest(int, int, int);
1085 static void PlayLevelSoundAction(int, int, int);
1086 static void PlayLevelSoundElementAction(int, int, int, int);
1087 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1088 static void PlayLevelSoundActionIfLoop(int, int, int);
1089 static void StopLevelSoundActionIfLoop(int, int, int);
1090 static void PlayLevelMusic(void);
1091 static void FadeLevelSoundsAndMusic(void);
1092
1093 static void HandleGameButtons(struct GadgetInfo *);
1094
1095 int AmoebaNeighbourNr(int, int);
1096 void AmoebaToDiamond(int, int);
1097 void ContinueMoving(int, int);
1098 void Bang(int, int);
1099 void InitMovDir(int, int);
1100 void InitAmoebaNr(int, int);
1101 int NewHiScore(int);
1102
1103 void TestIfGoodThingHitsBadThing(int, int, int);
1104 void TestIfBadThingHitsGoodThing(int, int, int);
1105 void TestIfPlayerTouchesBadThing(int, int);
1106 void TestIfPlayerRunsIntoBadThing(int, int, int);
1107 void TestIfBadThingTouchesPlayer(int, int);
1108 void TestIfBadThingRunsIntoPlayer(int, int, int);
1109 void TestIfFriendTouchesBadThing(int, int);
1110 void TestIfBadThingTouchesFriend(int, int);
1111 void TestIfBadThingTouchesOtherBadThing(int, int);
1112 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1113
1114 void KillPlayer(struct PlayerInfo *);
1115 void BuryPlayer(struct PlayerInfo *);
1116 void RemovePlayer(struct PlayerInfo *);
1117 void ExitPlayer(struct PlayerInfo *);
1118
1119 static int getInvisibleActiveFromInvisibleElement(int);
1120 static int getInvisibleFromInvisibleActiveElement(int);
1121
1122 static void TestFieldAfterSnapping(int, int, int, int, int);
1123
1124 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1125
1126 // for detection of endless loops, caused by custom element programming
1127 // (using maximal playfield width x 10 is just a rough approximation)
1128 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1129
1130 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1131 {                                                                       \
1132   if (recursion_loop_detected)                                          \
1133     return (rc);                                                        \
1134                                                                         \
1135   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1136   {                                                                     \
1137     recursion_loop_detected = TRUE;                                     \
1138     recursion_loop_element = (e);                                       \
1139   }                                                                     \
1140                                                                         \
1141   recursion_loop_depth++;                                               \
1142 }
1143
1144 #define RECURSION_LOOP_DETECTION_END()                                  \
1145 {                                                                       \
1146   recursion_loop_depth--;                                               \
1147 }
1148
1149 static int recursion_loop_depth;
1150 static boolean recursion_loop_detected;
1151 static boolean recursion_loop_element;
1152
1153 static int map_player_action[MAX_PLAYERS];
1154
1155
1156 // ----------------------------------------------------------------------------
1157 // definition of elements that automatically change to other elements after
1158 // a specified time, eventually calling a function when changing
1159 // ----------------------------------------------------------------------------
1160
1161 // forward declaration for changer functions
1162 static void InitBuggyBase(int, int);
1163 static void WarnBuggyBase(int, int);
1164
1165 static void InitTrap(int, int);
1166 static void ActivateTrap(int, int);
1167 static void ChangeActiveTrap(int, int);
1168
1169 static void InitRobotWheel(int, int);
1170 static void RunRobotWheel(int, int);
1171 static void StopRobotWheel(int, int);
1172
1173 static void InitTimegateWheel(int, int);
1174 static void RunTimegateWheel(int, int);
1175
1176 static void InitMagicBallDelay(int, int);
1177 static void ActivateMagicBall(int, int);
1178
1179 struct ChangingElementInfo
1180 {
1181   int element;
1182   int target_element;
1183   int change_delay;
1184   void (*pre_change_function)(int x, int y);
1185   void (*change_function)(int x, int y);
1186   void (*post_change_function)(int x, int y);
1187 };
1188
1189 static struct ChangingElementInfo change_delay_list[] =
1190 {
1191   {
1192     EL_NUT_BREAKING,
1193     EL_EMERALD,
1194     6,
1195     NULL,
1196     NULL,
1197     NULL
1198   },
1199   {
1200     EL_PEARL_BREAKING,
1201     EL_EMPTY,
1202     8,
1203     NULL,
1204     NULL,
1205     NULL
1206   },
1207   {
1208     EL_EXIT_OPENING,
1209     EL_EXIT_OPEN,
1210     29,
1211     NULL,
1212     NULL,
1213     NULL
1214   },
1215   {
1216     EL_EXIT_CLOSING,
1217     EL_EXIT_CLOSED,
1218     29,
1219     NULL,
1220     NULL,
1221     NULL
1222   },
1223   {
1224     EL_STEEL_EXIT_OPENING,
1225     EL_STEEL_EXIT_OPEN,
1226     29,
1227     NULL,
1228     NULL,
1229     NULL
1230   },
1231   {
1232     EL_STEEL_EXIT_CLOSING,
1233     EL_STEEL_EXIT_CLOSED,
1234     29,
1235     NULL,
1236     NULL,
1237     NULL
1238   },
1239   {
1240     EL_EM_EXIT_OPENING,
1241     EL_EM_EXIT_OPEN,
1242     29,
1243     NULL,
1244     NULL,
1245     NULL
1246   },
1247   {
1248     EL_EM_EXIT_CLOSING,
1249     EL_EMPTY,
1250     29,
1251     NULL,
1252     NULL,
1253     NULL
1254   },
1255   {
1256     EL_EM_STEEL_EXIT_OPENING,
1257     EL_EM_STEEL_EXIT_OPEN,
1258     29,
1259     NULL,
1260     NULL,
1261     NULL
1262   },
1263   {
1264     EL_EM_STEEL_EXIT_CLOSING,
1265     EL_STEELWALL,
1266     29,
1267     NULL,
1268     NULL,
1269     NULL
1270   },
1271   {
1272     EL_SP_EXIT_OPENING,
1273     EL_SP_EXIT_OPEN,
1274     29,
1275     NULL,
1276     NULL,
1277     NULL
1278   },
1279   {
1280     EL_SP_EXIT_CLOSING,
1281     EL_SP_EXIT_CLOSED,
1282     29,
1283     NULL,
1284     NULL,
1285     NULL
1286   },
1287   {
1288     EL_SWITCHGATE_OPENING,
1289     EL_SWITCHGATE_OPEN,
1290     29,
1291     NULL,
1292     NULL,
1293     NULL
1294   },
1295   {
1296     EL_SWITCHGATE_CLOSING,
1297     EL_SWITCHGATE_CLOSED,
1298     29,
1299     NULL,
1300     NULL,
1301     NULL
1302   },
1303   {
1304     EL_TIMEGATE_OPENING,
1305     EL_TIMEGATE_OPEN,
1306     29,
1307     NULL,
1308     NULL,
1309     NULL
1310   },
1311   {
1312     EL_TIMEGATE_CLOSING,
1313     EL_TIMEGATE_CLOSED,
1314     29,
1315     NULL,
1316     NULL,
1317     NULL
1318   },
1319
1320   {
1321     EL_ACID_SPLASH_LEFT,
1322     EL_EMPTY,
1323     8,
1324     NULL,
1325     NULL,
1326     NULL
1327   },
1328   {
1329     EL_ACID_SPLASH_RIGHT,
1330     EL_EMPTY,
1331     8,
1332     NULL,
1333     NULL,
1334     NULL
1335   },
1336   {
1337     EL_SP_BUGGY_BASE,
1338     EL_SP_BUGGY_BASE_ACTIVATING,
1339     0,
1340     InitBuggyBase,
1341     NULL,
1342     NULL
1343   },
1344   {
1345     EL_SP_BUGGY_BASE_ACTIVATING,
1346     EL_SP_BUGGY_BASE_ACTIVE,
1347     0,
1348     InitBuggyBase,
1349     NULL,
1350     NULL
1351   },
1352   {
1353     EL_SP_BUGGY_BASE_ACTIVE,
1354     EL_SP_BUGGY_BASE,
1355     0,
1356     InitBuggyBase,
1357     WarnBuggyBase,
1358     NULL
1359   },
1360   {
1361     EL_TRAP,
1362     EL_TRAP_ACTIVE,
1363     0,
1364     InitTrap,
1365     NULL,
1366     ActivateTrap
1367   },
1368   {
1369     EL_TRAP_ACTIVE,
1370     EL_TRAP,
1371     31,
1372     NULL,
1373     ChangeActiveTrap,
1374     NULL
1375   },
1376   {
1377     EL_ROBOT_WHEEL_ACTIVE,
1378     EL_ROBOT_WHEEL,
1379     0,
1380     InitRobotWheel,
1381     RunRobotWheel,
1382     StopRobotWheel
1383   },
1384   {
1385     EL_TIMEGATE_SWITCH_ACTIVE,
1386     EL_TIMEGATE_SWITCH,
1387     0,
1388     InitTimegateWheel,
1389     RunTimegateWheel,
1390     NULL
1391   },
1392   {
1393     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1394     EL_DC_TIMEGATE_SWITCH,
1395     0,
1396     InitTimegateWheel,
1397     RunTimegateWheel,
1398     NULL
1399   },
1400   {
1401     EL_EMC_MAGIC_BALL_ACTIVE,
1402     EL_EMC_MAGIC_BALL_ACTIVE,
1403     0,
1404     InitMagicBallDelay,
1405     NULL,
1406     ActivateMagicBall
1407   },
1408   {
1409     EL_EMC_SPRING_BUMPER_ACTIVE,
1410     EL_EMC_SPRING_BUMPER,
1411     8,
1412     NULL,
1413     NULL,
1414     NULL
1415   },
1416   {
1417     EL_DIAGONAL_SHRINKING,
1418     EL_UNDEFINED,
1419     0,
1420     NULL,
1421     NULL,
1422     NULL
1423   },
1424   {
1425     EL_DIAGONAL_GROWING,
1426     EL_UNDEFINED,
1427     0,
1428     NULL,
1429     NULL,
1430     NULL,
1431   },
1432
1433   {
1434     EL_UNDEFINED,
1435     EL_UNDEFINED,
1436     -1,
1437     NULL,
1438     NULL,
1439     NULL
1440   }
1441 };
1442
1443 struct
1444 {
1445   int element;
1446   int push_delay_fixed, push_delay_random;
1447 }
1448 push_delay_list[] =
1449 {
1450   { EL_SPRING,                  0, 0 },
1451   { EL_BALLOON,                 0, 0 },
1452
1453   { EL_SOKOBAN_OBJECT,          2, 0 },
1454   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1455   { EL_SATELLITE,               2, 0 },
1456   { EL_SP_DISK_YELLOW,          2, 0 },
1457
1458   { EL_UNDEFINED,               0, 0 },
1459 };
1460
1461 struct
1462 {
1463   int element;
1464   int move_stepsize;
1465 }
1466 move_stepsize_list[] =
1467 {
1468   { EL_AMOEBA_DROP,             2 },
1469   { EL_AMOEBA_DROPPING,         2 },
1470   { EL_QUICKSAND_FILLING,       1 },
1471   { EL_QUICKSAND_EMPTYING,      1 },
1472   { EL_QUICKSAND_FAST_FILLING,  2 },
1473   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1474   { EL_MAGIC_WALL_FILLING,      2 },
1475   { EL_MAGIC_WALL_EMPTYING,     2 },
1476   { EL_BD_MAGIC_WALL_FILLING,   2 },
1477   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1478   { EL_DC_MAGIC_WALL_FILLING,   2 },
1479   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1480
1481   { EL_UNDEFINED,               0 },
1482 };
1483
1484 struct
1485 {
1486   int element;
1487   int count;
1488 }
1489 collect_count_list[] =
1490 {
1491   { EL_EMERALD,                 1 },
1492   { EL_BD_DIAMOND,              1 },
1493   { EL_EMERALD_YELLOW,          1 },
1494   { EL_EMERALD_RED,             1 },
1495   { EL_EMERALD_PURPLE,          1 },
1496   { EL_DIAMOND,                 3 },
1497   { EL_SP_INFOTRON,             1 },
1498   { EL_PEARL,                   5 },
1499   { EL_CRYSTAL,                 8 },
1500
1501   { EL_UNDEFINED,               0 },
1502 };
1503
1504 struct
1505 {
1506   int element;
1507   int direction;
1508 }
1509 access_direction_list[] =
1510 {
1511   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1512   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1513   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1514   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1515   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1516   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1517   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1518   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1519   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1520   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1521   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1522
1523   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1524   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1525   { EL_SP_PORT_UP,                                                   MV_DOWN },
1526   { EL_SP_PORT_DOWN,                                         MV_UP           },
1527   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1528   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1529   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1530   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1531   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1532   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1533   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1534   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1535   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1536   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1537   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1538   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1539   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1540   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1541   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1542
1543   { EL_UNDEFINED,                       MV_NONE                              }
1544 };
1545
1546 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1547
1548 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1549 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1550 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1551                                  IS_JUST_CHANGING(x, y))
1552
1553 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1554
1555 // static variables for playfield scan mode (scanning forward or backward)
1556 static int playfield_scan_start_x = 0;
1557 static int playfield_scan_start_y = 0;
1558 static int playfield_scan_delta_x = 1;
1559 static int playfield_scan_delta_y = 1;
1560
1561 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1562                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1563                                      (y) += playfield_scan_delta_y)     \
1564                                 for ((x) = playfield_scan_start_x;      \
1565                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1566                                      (x) += playfield_scan_delta_x)
1567
1568 #ifdef DEBUG
1569 void DEBUG_SetMaximumDynamite(void)
1570 {
1571   int i;
1572
1573   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1574     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1575       local_player->inventory_element[local_player->inventory_size++] =
1576         EL_DYNAMITE;
1577 }
1578 #endif
1579
1580 static void InitPlayfieldScanModeVars(void)
1581 {
1582   if (game.use_reverse_scan_direction)
1583   {
1584     playfield_scan_start_x = lev_fieldx - 1;
1585     playfield_scan_start_y = lev_fieldy - 1;
1586
1587     playfield_scan_delta_x = -1;
1588     playfield_scan_delta_y = -1;
1589   }
1590   else
1591   {
1592     playfield_scan_start_x = 0;
1593     playfield_scan_start_y = 0;
1594
1595     playfield_scan_delta_x = 1;
1596     playfield_scan_delta_y = 1;
1597   }
1598 }
1599
1600 static void InitPlayfieldScanMode(int mode)
1601 {
1602   game.use_reverse_scan_direction =
1603     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1604
1605   InitPlayfieldScanModeVars();
1606 }
1607
1608 static int get_move_delay_from_stepsize(int move_stepsize)
1609 {
1610   move_stepsize =
1611     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1612
1613   // make sure that stepsize value is always a power of 2
1614   move_stepsize = (1 << log_2(move_stepsize));
1615
1616   return TILEX / move_stepsize;
1617 }
1618
1619 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1620                                boolean init_game)
1621 {
1622   int player_nr = player->index_nr;
1623   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1624   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1625
1626   // do no immediately change move delay -- the player might just be moving
1627   player->move_delay_value_next = move_delay;
1628
1629   // information if player can move must be set separately
1630   player->cannot_move = cannot_move;
1631
1632   if (init_game)
1633   {
1634     player->move_delay       = game.initial_move_delay[player_nr];
1635     player->move_delay_value = game.initial_move_delay_value[player_nr];
1636
1637     player->move_delay_value_next = -1;
1638
1639     player->move_delay_reset_counter = 0;
1640   }
1641 }
1642
1643 void GetPlayerConfig(void)
1644 {
1645   GameFrameDelay = setup.game_frame_delay;
1646
1647   if (!audio.sound_available)
1648     setup.sound_simple = FALSE;
1649
1650   if (!audio.loops_available)
1651     setup.sound_loops = FALSE;
1652
1653   if (!audio.music_available)
1654     setup.sound_music = FALSE;
1655
1656   if (!video.fullscreen_available)
1657     setup.fullscreen = FALSE;
1658
1659   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1660
1661   SetAudioMode(setup.sound);
1662 }
1663
1664 int GetElementFromGroupElement(int element)
1665 {
1666   if (IS_GROUP_ELEMENT(element))
1667   {
1668     struct ElementGroupInfo *group = element_info[element].group;
1669     int last_anim_random_frame = gfx.anim_random_frame;
1670     int element_pos;
1671
1672     if (group->choice_mode == ANIM_RANDOM)
1673       gfx.anim_random_frame = RND(group->num_elements_resolved);
1674
1675     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1676                                     group->choice_mode, 0,
1677                                     group->choice_pos);
1678
1679     if (group->choice_mode == ANIM_RANDOM)
1680       gfx.anim_random_frame = last_anim_random_frame;
1681
1682     group->choice_pos++;
1683
1684     element = group->element_resolved[element_pos];
1685   }
1686
1687   return element;
1688 }
1689
1690 static void IncrementSokobanFieldsNeeded(void)
1691 {
1692   if (level.sb_fields_needed)
1693     game.sokoban_fields_still_needed++;
1694 }
1695
1696 static void IncrementSokobanObjectsNeeded(void)
1697 {
1698   if (level.sb_objects_needed)
1699     game.sokoban_objects_still_needed++;
1700 }
1701
1702 static void DecrementSokobanFieldsNeeded(void)
1703 {
1704   if (game.sokoban_fields_still_needed > 0)
1705     game.sokoban_fields_still_needed--;
1706 }
1707
1708 static void DecrementSokobanObjectsNeeded(void)
1709 {
1710   if (game.sokoban_objects_still_needed > 0)
1711     game.sokoban_objects_still_needed--;
1712 }
1713
1714 static void InitPlayerField(int x, int y, int element, boolean init_game)
1715 {
1716   if (element == EL_SP_MURPHY)
1717   {
1718     if (init_game)
1719     {
1720       if (stored_player[0].present)
1721       {
1722         Tile[x][y] = EL_SP_MURPHY_CLONE;
1723
1724         return;
1725       }
1726       else
1727       {
1728         stored_player[0].initial_element = element;
1729         stored_player[0].use_murphy = TRUE;
1730
1731         if (!level.use_artwork_element[0])
1732           stored_player[0].artwork_element = EL_SP_MURPHY;
1733       }
1734
1735       Tile[x][y] = EL_PLAYER_1;
1736     }
1737   }
1738
1739   if (init_game)
1740   {
1741     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1742     int jx = player->jx, jy = player->jy;
1743
1744     player->present = TRUE;
1745
1746     player->block_last_field = (element == EL_SP_MURPHY ?
1747                                 level.sp_block_last_field :
1748                                 level.block_last_field);
1749
1750     // ---------- initialize player's last field block delay ------------------
1751
1752     // always start with reliable default value (no adjustment needed)
1753     player->block_delay_adjustment = 0;
1754
1755     // special case 1: in Supaplex, Murphy blocks last field one more frame
1756     if (player->block_last_field && element == EL_SP_MURPHY)
1757       player->block_delay_adjustment = 1;
1758
1759     // special case 2: in game engines before 3.1.1, blocking was different
1760     if (game.use_block_last_field_bug)
1761       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1762
1763     if (!network.enabled || player->connected_network)
1764     {
1765       player->active = TRUE;
1766
1767       // remove potentially duplicate players
1768       if (StorePlayer[jx][jy] == Tile[x][y])
1769         StorePlayer[jx][jy] = 0;
1770
1771       StorePlayer[x][y] = Tile[x][y];
1772
1773 #if DEBUG_INIT_PLAYER
1774       Debug("game:init:player", "- player element %d activated",
1775             player->element_nr);
1776       Debug("game:init:player", "  (local player is %d and currently %s)",
1777             local_player->element_nr,
1778             local_player->active ? "active" : "not active");
1779     }
1780 #endif
1781
1782     Tile[x][y] = EL_EMPTY;
1783
1784     player->jx = player->last_jx = x;
1785     player->jy = player->last_jy = y;
1786   }
1787
1788   // always check if player was just killed and should be reanimated
1789   {
1790     int player_nr = GET_PLAYER_NR(element);
1791     struct PlayerInfo *player = &stored_player[player_nr];
1792
1793     if (player->active && player->killed)
1794       player->reanimated = TRUE; // if player was just killed, reanimate him
1795   }
1796 }
1797
1798 static void InitField(int x, int y, boolean init_game)
1799 {
1800   int element = Tile[x][y];
1801
1802   switch (element)
1803   {
1804     case EL_SP_MURPHY:
1805     case EL_PLAYER_1:
1806     case EL_PLAYER_2:
1807     case EL_PLAYER_3:
1808     case EL_PLAYER_4:
1809       InitPlayerField(x, y, element, init_game);
1810       break;
1811
1812     case EL_SOKOBAN_FIELD_PLAYER:
1813       element = Tile[x][y] = EL_PLAYER_1;
1814       InitField(x, y, init_game);
1815
1816       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1817       InitField(x, y, init_game);
1818       break;
1819
1820     case EL_SOKOBAN_FIELD_EMPTY:
1821       IncrementSokobanFieldsNeeded();
1822       break;
1823
1824     case EL_SOKOBAN_OBJECT:
1825       IncrementSokobanObjectsNeeded();
1826       break;
1827
1828     case EL_STONEBLOCK:
1829       if (x < lev_fieldx-1 && Tile[x+1][y] == EL_ACID)
1830         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1831       else if (x > 0 && Tile[x-1][y] == EL_ACID)
1832         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1833       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPLEFT)
1834         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1835       else if (y > 0 && Tile[x][y-1] == EL_ACID)
1836         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1837       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1838         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1839       break;
1840
1841     case EL_BUG:
1842     case EL_BUG_RIGHT:
1843     case EL_BUG_UP:
1844     case EL_BUG_LEFT:
1845     case EL_BUG_DOWN:
1846     case EL_SPACESHIP:
1847     case EL_SPACESHIP_RIGHT:
1848     case EL_SPACESHIP_UP:
1849     case EL_SPACESHIP_LEFT:
1850     case EL_SPACESHIP_DOWN:
1851     case EL_BD_BUTTERFLY:
1852     case EL_BD_BUTTERFLY_RIGHT:
1853     case EL_BD_BUTTERFLY_UP:
1854     case EL_BD_BUTTERFLY_LEFT:
1855     case EL_BD_BUTTERFLY_DOWN:
1856     case EL_BD_FIREFLY:
1857     case EL_BD_FIREFLY_RIGHT:
1858     case EL_BD_FIREFLY_UP:
1859     case EL_BD_FIREFLY_LEFT:
1860     case EL_BD_FIREFLY_DOWN:
1861     case EL_PACMAN_RIGHT:
1862     case EL_PACMAN_UP:
1863     case EL_PACMAN_LEFT:
1864     case EL_PACMAN_DOWN:
1865     case EL_YAMYAM:
1866     case EL_YAMYAM_LEFT:
1867     case EL_YAMYAM_RIGHT:
1868     case EL_YAMYAM_UP:
1869     case EL_YAMYAM_DOWN:
1870     case EL_DARK_YAMYAM:
1871     case EL_ROBOT:
1872     case EL_PACMAN:
1873     case EL_SP_SNIKSNAK:
1874     case EL_SP_ELECTRON:
1875     case EL_MOLE:
1876     case EL_MOLE_LEFT:
1877     case EL_MOLE_RIGHT:
1878     case EL_MOLE_UP:
1879     case EL_MOLE_DOWN:
1880     case EL_SPRING_LEFT:
1881     case EL_SPRING_RIGHT:
1882       InitMovDir(x, y);
1883       break;
1884
1885     case EL_AMOEBA_FULL:
1886     case EL_BD_AMOEBA:
1887       InitAmoebaNr(x, y);
1888       break;
1889
1890     case EL_AMOEBA_DROP:
1891       if (y == lev_fieldy - 1)
1892       {
1893         Tile[x][y] = EL_AMOEBA_GROWING;
1894         Store[x][y] = EL_AMOEBA_WET;
1895       }
1896       break;
1897
1898     case EL_DYNAMITE_ACTIVE:
1899     case EL_SP_DISK_RED_ACTIVE:
1900     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1901     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1902     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1903     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1904       MovDelay[x][y] = 96;
1905       break;
1906
1907     case EL_EM_DYNAMITE_ACTIVE:
1908       MovDelay[x][y] = 32;
1909       break;
1910
1911     case EL_LAMP:
1912       game.lights_still_needed++;
1913       break;
1914
1915     case EL_PENGUIN:
1916       game.friends_still_needed++;
1917       break;
1918
1919     case EL_PIG:
1920     case EL_DRAGON:
1921       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1922       break;
1923
1924     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1925     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1926     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1927     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1928     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1929     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1930     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1931     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1932     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1933     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1934     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1935     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1936       if (init_game)
1937       {
1938         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1939         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1940         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1941
1942         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1943         {
1944           game.belt_dir[belt_nr] = belt_dir;
1945           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1946         }
1947         else    // more than one switch -- set it like the first switch
1948         {
1949           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1950         }
1951       }
1952       break;
1953
1954     case EL_LIGHT_SWITCH_ACTIVE:
1955       if (init_game)
1956         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1957       break;
1958
1959     case EL_INVISIBLE_STEELWALL:
1960     case EL_INVISIBLE_WALL:
1961     case EL_INVISIBLE_SAND:
1962       if (game.light_time_left > 0 ||
1963           game.lenses_time_left > 0)
1964         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1965       break;
1966
1967     case EL_EMC_MAGIC_BALL:
1968       if (game.ball_active)
1969         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1970       break;
1971
1972     case EL_EMC_MAGIC_BALL_SWITCH:
1973       if (game.ball_active)
1974         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1975       break;
1976
1977     case EL_TRIGGER_PLAYER:
1978     case EL_TRIGGER_ELEMENT:
1979     case EL_TRIGGER_CE_VALUE:
1980     case EL_TRIGGER_CE_SCORE:
1981     case EL_SELF:
1982     case EL_ANY_ELEMENT:
1983     case EL_CURRENT_CE_VALUE:
1984     case EL_CURRENT_CE_SCORE:
1985     case EL_PREV_CE_1:
1986     case EL_PREV_CE_2:
1987     case EL_PREV_CE_3:
1988     case EL_PREV_CE_4:
1989     case EL_PREV_CE_5:
1990     case EL_PREV_CE_6:
1991     case EL_PREV_CE_7:
1992     case EL_PREV_CE_8:
1993     case EL_NEXT_CE_1:
1994     case EL_NEXT_CE_2:
1995     case EL_NEXT_CE_3:
1996     case EL_NEXT_CE_4:
1997     case EL_NEXT_CE_5:
1998     case EL_NEXT_CE_6:
1999     case EL_NEXT_CE_7:
2000     case EL_NEXT_CE_8:
2001       // reference elements should not be used on the playfield
2002       Tile[x][y] = EL_EMPTY;
2003       break;
2004
2005     default:
2006       if (IS_CUSTOM_ELEMENT(element))
2007       {
2008         if (CAN_MOVE(element))
2009           InitMovDir(x, y);
2010
2011         if (!element_info[element].use_last_ce_value || init_game)
2012           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2013       }
2014       else if (IS_GROUP_ELEMENT(element))
2015       {
2016         Tile[x][y] = GetElementFromGroupElement(element);
2017
2018         InitField(x, y, init_game);
2019       }
2020
2021       break;
2022   }
2023
2024   if (!init_game)
2025     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2026 }
2027
2028 static void InitField_WithBug1(int x, int y, boolean init_game)
2029 {
2030   InitField(x, y, init_game);
2031
2032   // not needed to call InitMovDir() -- already done by InitField()!
2033   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2034       CAN_MOVE(Tile[x][y]))
2035     InitMovDir(x, y);
2036 }
2037
2038 static void InitField_WithBug2(int x, int y, boolean init_game)
2039 {
2040   int old_element = Tile[x][y];
2041
2042   InitField(x, y, init_game);
2043
2044   // not needed to call InitMovDir() -- already done by InitField()!
2045   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2046       CAN_MOVE(old_element) &&
2047       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2048     InitMovDir(x, y);
2049
2050   /* this case is in fact a combination of not less than three bugs:
2051      first, it calls InitMovDir() for elements that can move, although this is
2052      already done by InitField(); then, it checks the element that was at this
2053      field _before_ the call to InitField() (which can change it); lastly, it
2054      was not called for "mole with direction" elements, which were treated as
2055      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2056   */
2057 }
2058
2059 static int get_key_element_from_nr(int key_nr)
2060 {
2061   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2062                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2063                           EL_EM_KEY_1 : EL_KEY_1);
2064
2065   return key_base_element + key_nr;
2066 }
2067
2068 static int get_next_dropped_element(struct PlayerInfo *player)
2069 {
2070   return (player->inventory_size > 0 ?
2071           player->inventory_element[player->inventory_size - 1] :
2072           player->inventory_infinite_element != EL_UNDEFINED ?
2073           player->inventory_infinite_element :
2074           player->dynabombs_left > 0 ?
2075           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2076           EL_UNDEFINED);
2077 }
2078
2079 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2080 {
2081   // pos >= 0: get element from bottom of the stack;
2082   // pos <  0: get element from top of the stack
2083
2084   if (pos < 0)
2085   {
2086     int min_inventory_size = -pos;
2087     int inventory_pos = player->inventory_size - min_inventory_size;
2088     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2089
2090     return (player->inventory_size >= min_inventory_size ?
2091             player->inventory_element[inventory_pos] :
2092             player->inventory_infinite_element != EL_UNDEFINED ?
2093             player->inventory_infinite_element :
2094             player->dynabombs_left >= min_dynabombs_left ?
2095             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2096             EL_UNDEFINED);
2097   }
2098   else
2099   {
2100     int min_dynabombs_left = pos + 1;
2101     int min_inventory_size = pos + 1 - player->dynabombs_left;
2102     int inventory_pos = pos - player->dynabombs_left;
2103
2104     return (player->inventory_infinite_element != EL_UNDEFINED ?
2105             player->inventory_infinite_element :
2106             player->dynabombs_left >= min_dynabombs_left ?
2107             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2108             player->inventory_size >= min_inventory_size ?
2109             player->inventory_element[inventory_pos] :
2110             EL_UNDEFINED);
2111   }
2112 }
2113
2114 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2115 {
2116   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2117   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2118   int compare_result;
2119
2120   if (gpo1->sort_priority != gpo2->sort_priority)
2121     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2122   else
2123     compare_result = gpo1->nr - gpo2->nr;
2124
2125   return compare_result;
2126 }
2127
2128 int getPlayerInventorySize(int player_nr)
2129 {
2130   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2131     return game_em.ply[player_nr]->dynamite;
2132   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2133     return game_sp.red_disk_count;
2134   else
2135     return stored_player[player_nr].inventory_size;
2136 }
2137
2138 static void InitGameControlValues(void)
2139 {
2140   int i;
2141
2142   for (i = 0; game_panel_controls[i].nr != -1; i++)
2143   {
2144     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2145     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2146     struct TextPosInfo *pos = gpc->pos;
2147     int nr = gpc->nr;
2148     int type = gpc->type;
2149
2150     if (nr != i)
2151     {
2152       Error("'game_panel_controls' structure corrupted at %d", i);
2153
2154       Fail("this should not happen -- please debug");
2155     }
2156
2157     // force update of game controls after initialization
2158     gpc->value = gpc->last_value = -1;
2159     gpc->frame = gpc->last_frame = -1;
2160     gpc->gfx_frame = -1;
2161
2162     // determine panel value width for later calculation of alignment
2163     if (type == TYPE_INTEGER || type == TYPE_STRING)
2164     {
2165       pos->width = pos->size * getFontWidth(pos->font);
2166       pos->height = getFontHeight(pos->font);
2167     }
2168     else if (type == TYPE_ELEMENT)
2169     {
2170       pos->width = pos->size;
2171       pos->height = pos->size;
2172     }
2173
2174     // fill structure for game panel draw order
2175     gpo->nr = gpc->nr;
2176     gpo->sort_priority = pos->sort_priority;
2177   }
2178
2179   // sort game panel controls according to sort_priority and control number
2180   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2181         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2182 }
2183
2184 static void UpdatePlayfieldElementCount(void)
2185 {
2186   boolean use_element_count = FALSE;
2187   int i, j, x, y;
2188
2189   // first check if it is needed at all to calculate playfield element count
2190   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2191     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2192       use_element_count = TRUE;
2193
2194   if (!use_element_count)
2195     return;
2196
2197   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2198     element_info[i].element_count = 0;
2199
2200   SCAN_PLAYFIELD(x, y)
2201   {
2202     element_info[Tile[x][y]].element_count++;
2203   }
2204
2205   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2206     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2207       if (IS_IN_GROUP(j, i))
2208         element_info[EL_GROUP_START + i].element_count +=
2209           element_info[j].element_count;
2210 }
2211
2212 static void UpdateGameControlValues(void)
2213 {
2214   int i, k;
2215   int time = (game.LevelSolved ?
2216               game.LevelSolved_CountingTime :
2217               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2218               game_em.lev->time :
2219               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2220               game_sp.time_played :
2221               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2222               game_mm.energy_left :
2223               game.no_time_limit ? TimePlayed : TimeLeft);
2224   int score = (game.LevelSolved ?
2225                game.LevelSolved_CountingScore :
2226                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2227                game_em.lev->score :
2228                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2229                game_sp.score :
2230                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2231                game_mm.score :
2232                game.score);
2233   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2234               game_em.lev->gems_needed :
2235               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2236               game_sp.infotrons_still_needed :
2237               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2238               game_mm.kettles_still_needed :
2239               game.gems_still_needed);
2240   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2241                      game_em.lev->gems_needed > 0 :
2242                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2243                      game_sp.infotrons_still_needed > 0 :
2244                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2245                      game_mm.kettles_still_needed > 0 ||
2246                      game_mm.lights_still_needed > 0 :
2247                      game.gems_still_needed > 0 ||
2248                      game.sokoban_fields_still_needed > 0 ||
2249                      game.sokoban_objects_still_needed > 0 ||
2250                      game.lights_still_needed > 0);
2251   int health = (game.LevelSolved ?
2252                 game.LevelSolved_CountingHealth :
2253                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2254                 MM_HEALTH(game_mm.laser_overload_value) :
2255                 game.health);
2256   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2257
2258   UpdatePlayfieldElementCount();
2259
2260   // update game panel control values
2261
2262   // used instead of "level_nr" (for network games)
2263   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2264   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2265
2266   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2267   for (i = 0; i < MAX_NUM_KEYS; i++)
2268     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2269   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2270   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2271
2272   if (game.centered_player_nr == -1)
2273   {
2274     for (i = 0; i < MAX_PLAYERS; i++)
2275     {
2276       // only one player in Supaplex game engine
2277       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2278         break;
2279
2280       for (k = 0; k < MAX_NUM_KEYS; k++)
2281       {
2282         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2283         {
2284           if (game_em.ply[i]->keys & (1 << k))
2285             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2286               get_key_element_from_nr(k);
2287         }
2288         else if (stored_player[i].key[k])
2289           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2290             get_key_element_from_nr(k);
2291       }
2292
2293       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2294         getPlayerInventorySize(i);
2295
2296       if (stored_player[i].num_white_keys > 0)
2297         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2298           EL_DC_KEY_WHITE;
2299
2300       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2301         stored_player[i].num_white_keys;
2302     }
2303   }
2304   else
2305   {
2306     int player_nr = game.centered_player_nr;
2307
2308     for (k = 0; k < MAX_NUM_KEYS; k++)
2309     {
2310       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2311       {
2312         if (game_em.ply[player_nr]->keys & (1 << k))
2313           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2314             get_key_element_from_nr(k);
2315       }
2316       else if (stored_player[player_nr].key[k])
2317         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2318           get_key_element_from_nr(k);
2319     }
2320
2321     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2322       getPlayerInventorySize(player_nr);
2323
2324     if (stored_player[player_nr].num_white_keys > 0)
2325       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2326
2327     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2328       stored_player[player_nr].num_white_keys;
2329   }
2330
2331   // re-arrange keys on game panel, if needed or if defined by style settings
2332   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2333   {
2334     int nr = GAME_PANEL_KEY_1 + i;
2335     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2336     struct TextPosInfo *pos = gpc->pos;
2337
2338     // skip check if key is not in the player's inventory
2339     if (gpc->value == EL_EMPTY)
2340       continue;
2341
2342     // check if keys should be arranged on panel from left to right
2343     if (pos->style == STYLE_LEFTMOST_POSITION)
2344     {
2345       // check previous key positions (left from current key)
2346       for (k = 0; k < i; k++)
2347       {
2348         int nr_new = GAME_PANEL_KEY_1 + k;
2349
2350         if (game_panel_controls[nr_new].value == EL_EMPTY)
2351         {
2352           game_panel_controls[nr_new].value = gpc->value;
2353           gpc->value = EL_EMPTY;
2354
2355           break;
2356         }
2357       }
2358     }
2359
2360     // check if "undefined" keys can be placed at some other position
2361     if (pos->x == -1 && pos->y == -1)
2362     {
2363       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2364
2365       // 1st try: display key at the same position as normal or EM keys
2366       if (game_panel_controls[nr_new].value == EL_EMPTY)
2367       {
2368         game_panel_controls[nr_new].value = gpc->value;
2369       }
2370       else
2371       {
2372         // 2nd try: display key at the next free position in the key panel
2373         for (k = 0; k < STD_NUM_KEYS; k++)
2374         {
2375           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
2381             break;
2382           }
2383         }
2384       }
2385     }
2386   }
2387
2388   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2389   {
2390     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2391       get_inventory_element_from_pos(local_player, i);
2392     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2393       get_inventory_element_from_pos(local_player, -i - 1);
2394   }
2395
2396   game_panel_controls[GAME_PANEL_SCORE].value = score;
2397   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2398
2399   game_panel_controls[GAME_PANEL_TIME].value = time;
2400
2401   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2402   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2403   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2404
2405   if (level.time == 0)
2406     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2407   else
2408     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2409
2410   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2411   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2412
2413   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2414
2415   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2416     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2417      EL_EMPTY);
2418   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2419     local_player->shield_normal_time_left;
2420   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2421     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2422      EL_EMPTY);
2423   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2424     local_player->shield_deadly_time_left;
2425
2426   game_panel_controls[GAME_PANEL_EXIT].value =
2427     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2428
2429   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2430     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2431   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2432     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2433      EL_EMC_MAGIC_BALL_SWITCH);
2434
2435   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2436     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2437   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2438     game.light_time_left;
2439
2440   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2441     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2442   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2443     game.timegate_time_left;
2444
2445   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2446     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2447
2448   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2449     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2450   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2451     game.lenses_time_left;
2452
2453   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2454     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2455   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2456     game.magnify_time_left;
2457
2458   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2459     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2460      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2461      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2462      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2463      EL_BALLOON_SWITCH_NONE);
2464
2465   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2466     local_player->dynabomb_count;
2467   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2468     local_player->dynabomb_size;
2469   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2470     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2471
2472   game_panel_controls[GAME_PANEL_PENGUINS].value =
2473     game.friends_still_needed;
2474
2475   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2476     game.sokoban_objects_still_needed;
2477   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2478     game.sokoban_fields_still_needed;
2479
2480   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2481     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2482
2483   for (i = 0; i < NUM_BELTS; i++)
2484   {
2485     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2486       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2487        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2488     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2489       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2490   }
2491
2492   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2493     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2494   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2495     game.magic_wall_time_left;
2496
2497   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2498     local_player->gravity;
2499
2500   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2501     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2502
2503   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2504     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2505       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2506        game.panel.element[i].id : EL_UNDEFINED);
2507
2508   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2509     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2510       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2511        element_info[game.panel.element_count[i].id].element_count : 0);
2512
2513   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2514     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2515       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2516        element_info[game.panel.ce_score[i].id].collect_score : 0);
2517
2518   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2519     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2520       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2521        element_info[game.panel.ce_score_element[i].id].collect_score :
2522        EL_UNDEFINED);
2523
2524   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2525   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2526   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2527
2528   // update game panel control frames
2529
2530   for (i = 0; game_panel_controls[i].nr != -1; i++)
2531   {
2532     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2533
2534     if (gpc->type == TYPE_ELEMENT)
2535     {
2536       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2537       {
2538         int last_anim_random_frame = gfx.anim_random_frame;
2539         int element = gpc->value;
2540         int graphic = el2panelimg(element);
2541         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2542                                sync_random_frame : INIT_GFX_RANDOM());
2543
2544         if (gpc->value != gpc->last_value)
2545         {
2546           gpc->gfx_frame = 0;
2547           gpc->gfx_random = init_gfx_random;
2548         }
2549         else
2550         {
2551           gpc->gfx_frame++;
2552
2553           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2554               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2555             gpc->gfx_random = init_gfx_random;
2556         }
2557
2558         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2559           gfx.anim_random_frame = gpc->gfx_random;
2560
2561         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2562           gpc->gfx_frame = element_info[element].collect_score;
2563
2564         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2565
2566         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2567           gfx.anim_random_frame = last_anim_random_frame;
2568       }
2569     }
2570     else if (gpc->type == TYPE_GRAPHIC)
2571     {
2572       if (gpc->graphic != IMG_UNDEFINED)
2573       {
2574         int last_anim_random_frame = gfx.anim_random_frame;
2575         int graphic = gpc->graphic;
2576         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2577                                sync_random_frame : INIT_GFX_RANDOM());
2578
2579         if (gpc->value != gpc->last_value)
2580         {
2581           gpc->gfx_frame = 0;
2582           gpc->gfx_random = init_gfx_random;
2583         }
2584         else
2585         {
2586           gpc->gfx_frame++;
2587
2588           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2589               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2590             gpc->gfx_random = init_gfx_random;
2591         }
2592
2593         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2594           gfx.anim_random_frame = gpc->gfx_random;
2595
2596         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2597
2598         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2599           gfx.anim_random_frame = last_anim_random_frame;
2600       }
2601     }
2602   }
2603 }
2604
2605 static void DisplayGameControlValues(void)
2606 {
2607   boolean redraw_panel = FALSE;
2608   int i;
2609
2610   for (i = 0; game_panel_controls[i].nr != -1; i++)
2611   {
2612     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2613
2614     if (PANEL_DEACTIVATED(gpc->pos))
2615       continue;
2616
2617     if (gpc->value == gpc->last_value &&
2618         gpc->frame == gpc->last_frame)
2619       continue;
2620
2621     redraw_panel = TRUE;
2622   }
2623
2624   if (!redraw_panel)
2625     return;
2626
2627   // copy default game door content to main double buffer
2628
2629   // !!! CHECK AGAIN !!!
2630   SetPanelBackground();
2631   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2632   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2633
2634   // redraw game control buttons
2635   RedrawGameButtons();
2636
2637   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2638
2639   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2640   {
2641     int nr = game_panel_order[i].nr;
2642     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2643     struct TextPosInfo *pos = gpc->pos;
2644     int type = gpc->type;
2645     int value = gpc->value;
2646     int frame = gpc->frame;
2647     int size = pos->size;
2648     int font = pos->font;
2649     boolean draw_masked = pos->draw_masked;
2650     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2651
2652     if (PANEL_DEACTIVATED(pos))
2653       continue;
2654
2655     if (pos->class == get_hash_from_key("extra_panel_items") &&
2656         !setup.prefer_extra_panel_items)
2657       continue;
2658
2659     gpc->last_value = value;
2660     gpc->last_frame = frame;
2661
2662     if (type == TYPE_INTEGER)
2663     {
2664       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2665           nr == GAME_PANEL_TIME)
2666       {
2667         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2668
2669         if (use_dynamic_size)           // use dynamic number of digits
2670         {
2671           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2672           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2673           int size2 = size1 + 1;
2674           int font1 = pos->font;
2675           int font2 = pos->font_alt;
2676
2677           size = (value < value_change ? size1 : size2);
2678           font = (value < value_change ? font1 : font2);
2679         }
2680       }
2681
2682       // correct text size if "digits" is zero or less
2683       if (size <= 0)
2684         size = strlen(int2str(value, size));
2685
2686       // dynamically correct text alignment
2687       pos->width = size * getFontWidth(font);
2688
2689       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2690                   int2str(value, size), font, mask_mode);
2691     }
2692     else if (type == TYPE_ELEMENT)
2693     {
2694       int element, graphic;
2695       Bitmap *src_bitmap;
2696       int src_x, src_y;
2697       int width, height;
2698       int dst_x = PANEL_XPOS(pos);
2699       int dst_y = PANEL_YPOS(pos);
2700
2701       if (value != EL_UNDEFINED && value != EL_EMPTY)
2702       {
2703         element = value;
2704         graphic = el2panelimg(value);
2705
2706 #if 0
2707         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2708               element, EL_NAME(element), size);
2709 #endif
2710
2711         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2712           size = TILESIZE;
2713
2714         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2715                               &src_x, &src_y);
2716
2717         width  = graphic_info[graphic].width  * size / TILESIZE;
2718         height = graphic_info[graphic].height * size / TILESIZE;
2719
2720         if (draw_masked)
2721           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2722                            dst_x, dst_y);
2723         else
2724           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2725                      dst_x, dst_y);
2726       }
2727     }
2728     else if (type == TYPE_GRAPHIC)
2729     {
2730       int graphic        = gpc->graphic;
2731       int graphic_active = gpc->graphic_active;
2732       Bitmap *src_bitmap;
2733       int src_x, src_y;
2734       int width, height;
2735       int dst_x = PANEL_XPOS(pos);
2736       int dst_y = PANEL_YPOS(pos);
2737       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2738                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2739
2740       if (graphic != IMG_UNDEFINED && !skip)
2741       {
2742         if (pos->style == STYLE_REVERSE)
2743           value = 100 - value;
2744
2745         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2746
2747         if (pos->direction & MV_HORIZONTAL)
2748         {
2749           width  = graphic_info[graphic_active].width * value / 100;
2750           height = graphic_info[graphic_active].height;
2751
2752           if (pos->direction == MV_LEFT)
2753           {
2754             src_x += graphic_info[graphic_active].width - width;
2755             dst_x += graphic_info[graphic_active].width - width;
2756           }
2757         }
2758         else
2759         {
2760           width  = graphic_info[graphic_active].width;
2761           height = graphic_info[graphic_active].height * value / 100;
2762
2763           if (pos->direction == MV_UP)
2764           {
2765             src_y += graphic_info[graphic_active].height - height;
2766             dst_y += graphic_info[graphic_active].height - height;
2767           }
2768         }
2769
2770         if (draw_masked)
2771           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2772                            dst_x, dst_y);
2773         else
2774           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2775                      dst_x, dst_y);
2776
2777         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2778
2779         if (pos->direction & MV_HORIZONTAL)
2780         {
2781           if (pos->direction == MV_RIGHT)
2782           {
2783             src_x += width;
2784             dst_x += width;
2785           }
2786           else
2787           {
2788             dst_x = PANEL_XPOS(pos);
2789           }
2790
2791           width = graphic_info[graphic].width - width;
2792         }
2793         else
2794         {
2795           if (pos->direction == MV_DOWN)
2796           {
2797             src_y += height;
2798             dst_y += height;
2799           }
2800           else
2801           {
2802             dst_y = PANEL_YPOS(pos);
2803           }
2804
2805           height = graphic_info[graphic].height - height;
2806         }
2807
2808         if (draw_masked)
2809           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2810                            dst_x, dst_y);
2811         else
2812           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2813                      dst_x, dst_y);
2814       }
2815     }
2816     else if (type == TYPE_STRING)
2817     {
2818       boolean active = (value != 0);
2819       char *state_normal = "off";
2820       char *state_active = "on";
2821       char *state = (active ? state_active : state_normal);
2822       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2823                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2824                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2825                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2826
2827       if (nr == GAME_PANEL_GRAVITY_STATE)
2828       {
2829         int font1 = pos->font;          // (used for normal state)
2830         int font2 = pos->font_alt;      // (used for active state)
2831
2832         font = (active ? font2 : font1);
2833       }
2834
2835       if (s != NULL)
2836       {
2837         char *s_cut;
2838
2839         if (size <= 0)
2840         {
2841           // don't truncate output if "chars" is zero or less
2842           size = strlen(s);
2843
2844           // dynamically correct text alignment
2845           pos->width = size * getFontWidth(font);
2846         }
2847
2848         s_cut = getStringCopyN(s, size);
2849
2850         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2851                     s_cut, font, mask_mode);
2852
2853         free(s_cut);
2854       }
2855     }
2856
2857     redraw_mask |= REDRAW_DOOR_1;
2858   }
2859
2860   SetGameStatus(GAME_MODE_PLAYING);
2861 }
2862
2863 void UpdateAndDisplayGameControlValues(void)
2864 {
2865   if (tape.deactivate_display)
2866     return;
2867
2868   UpdateGameControlValues();
2869   DisplayGameControlValues();
2870 }
2871
2872 #if 0
2873 static void UpdateGameDoorValues(void)
2874 {
2875   UpdateGameControlValues();
2876 }
2877 #endif
2878
2879 void DrawGameDoorValues(void)
2880 {
2881   DisplayGameControlValues();
2882 }
2883
2884
2885 // ============================================================================
2886 // InitGameEngine()
2887 // ----------------------------------------------------------------------------
2888 // initialize game engine due to level / tape version number
2889 // ============================================================================
2890
2891 static void InitGameEngine(void)
2892 {
2893   int i, j, k, l, x, y;
2894
2895   // set game engine from tape file when re-playing, else from level file
2896   game.engine_version = (tape.playing ? tape.engine_version :
2897                          level.game_version);
2898
2899   // set single or multi-player game mode (needed for re-playing tapes)
2900   game.team_mode = setup.team_mode;
2901
2902   if (tape.playing)
2903   {
2904     int num_players = 0;
2905
2906     for (i = 0; i < MAX_PLAYERS; i++)
2907       if (tape.player_participates[i])
2908         num_players++;
2909
2910     // multi-player tapes contain input data for more than one player
2911     game.team_mode = (num_players > 1);
2912   }
2913
2914 #if 0
2915   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2916         level.game_version);
2917   Debug("game:init:level", "          tape.file_version   == %06d",
2918         tape.file_version);
2919   Debug("game:init:level", "          tape.game_version   == %06d",
2920         tape.game_version);
2921   Debug("game:init:level", "          tape.engine_version == %06d",
2922         tape.engine_version);
2923   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2924         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2925 #endif
2926
2927   // --------------------------------------------------------------------------
2928   // set flags for bugs and changes according to active game engine version
2929   // --------------------------------------------------------------------------
2930
2931   /*
2932     Summary of bugfix:
2933     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2934
2935     Bug was introduced in version:
2936     2.0.1
2937
2938     Bug was fixed in version:
2939     4.2.0.0
2940
2941     Description:
2942     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2943     but the property "can fall" was missing, which caused some levels to be
2944     unsolvable. This was fixed in version 4.2.0.0.
2945
2946     Affected levels/tapes:
2947     An example for a tape that was fixed by this bugfix is tape 029 from the
2948     level set "rnd_sam_bateman".
2949     The wrong behaviour will still be used for all levels or tapes that were
2950     created/recorded with it. An example for this is tape 023 from the level
2951     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2952   */
2953
2954   boolean use_amoeba_dropping_cannot_fall_bug =
2955     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2956       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2957      (tape.playing &&
2958       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2959       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2960
2961   /*
2962     Summary of bugfix/change:
2963     Fixed move speed of elements entering or leaving magic wall.
2964
2965     Fixed/changed in version:
2966     2.0.1
2967
2968     Description:
2969     Before 2.0.1, move speed of elements entering or leaving magic wall was
2970     twice as fast as it is now.
2971     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2972
2973     Affected levels/tapes:
2974     The first condition is generally needed for all levels/tapes before version
2975     2.0.1, which might use the old behaviour before it was changed; known tapes
2976     that are affected: Tape 014 from the level set "rnd_conor_mancone".
2977     The second condition is an exception from the above case and is needed for
2978     the special case of tapes recorded with game (not engine!) version 2.0.1 or
2979     above, but before it was known that this change would break tapes like the
2980     above and was fixed in 4.2.0.0, so that the changed behaviour was active
2981     although the engine version while recording maybe was before 2.0.1. There
2982     are a lot of tapes that are affected by this exception, like tape 006 from
2983     the level set "rnd_conor_mancone".
2984   */
2985
2986   boolean use_old_move_stepsize_for_magic_wall =
2987     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
2988      !(tape.playing &&
2989        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2990        tape.game_version <  VERSION_IDENT(4,2,0,0)));
2991
2992   /*
2993     Summary of bugfix/change:
2994     Fixed handling for custom elements that change when pushed by the player.
2995
2996     Fixed/changed in version:
2997     3.1.0
2998
2999     Description:
3000     Before 3.1.0, custom elements that "change when pushing" changed directly
3001     after the player started pushing them (until then handled in "DigField()").
3002     Since 3.1.0, these custom elements are not changed until the "pushing"
3003     move of the element is finished (now handled in "ContinueMoving()").
3004
3005     Affected levels/tapes:
3006     The first condition is generally needed for all levels/tapes before version
3007     3.1.0, which might use the old behaviour before it was changed; known tapes
3008     that are affected are some tapes from the level set "Walpurgis Gardens" by
3009     Jamie Cullen.
3010     The second condition is an exception from the above case and is needed for
3011     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3012     above (including some development versions of 3.1.0), but before it was
3013     known that this change would break tapes like the above and was fixed in
3014     3.1.1, so that the changed behaviour was active although the engine version
3015     while recording maybe was before 3.1.0. There is at least one tape that is
3016     affected by this exception, which is the tape for the one-level set "Bug
3017     Machine" by Juergen Bonhagen.
3018   */
3019
3020   game.use_change_when_pushing_bug =
3021     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3022      !(tape.playing &&
3023        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3024        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3025
3026   /*
3027     Summary of bugfix/change:
3028     Fixed handling for blocking the field the player leaves when moving.
3029
3030     Fixed/changed in version:
3031     3.1.1
3032
3033     Description:
3034     Before 3.1.1, when "block last field when moving" was enabled, the field
3035     the player is leaving when moving was blocked for the time of the move,
3036     and was directly unblocked afterwards. This resulted in the last field
3037     being blocked for exactly one less than the number of frames of one player
3038     move. Additionally, even when blocking was disabled, the last field was
3039     blocked for exactly one frame.
3040     Since 3.1.1, due to changes in player movement handling, the last field
3041     is not blocked at all when blocking is disabled. When blocking is enabled,
3042     the last field is blocked for exactly the number of frames of one player
3043     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3044     last field is blocked for exactly one more than the number of frames of
3045     one player move.
3046
3047     Affected levels/tapes:
3048     (!!! yet to be determined -- probably many !!!)
3049   */
3050
3051   game.use_block_last_field_bug =
3052     (game.engine_version < VERSION_IDENT(3,1,1,0));
3053
3054   /* various special flags and settings for native Emerald Mine game engine */
3055
3056   game_em.use_single_button =
3057     (game.engine_version > VERSION_IDENT(4,0,0,2));
3058
3059   game_em.use_snap_key_bug =
3060     (game.engine_version < VERSION_IDENT(4,0,1,0));
3061
3062   game_em.use_random_bug =
3063     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3064
3065   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3066
3067   game_em.use_old_explosions            = use_old_em_engine;
3068   game_em.use_old_android               = use_old_em_engine;
3069   game_em.use_old_push_elements         = use_old_em_engine;
3070   game_em.use_old_push_into_acid        = use_old_em_engine;
3071
3072   game_em.use_wrap_around               = !use_old_em_engine;
3073
3074   // --------------------------------------------------------------------------
3075
3076   // set maximal allowed number of custom element changes per game frame
3077   game.max_num_changes_per_frame = 1;
3078
3079   // default scan direction: scan playfield from top/left to bottom/right
3080   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3081
3082   // dynamically adjust element properties according to game engine version
3083   InitElementPropertiesEngine(game.engine_version);
3084
3085   // ---------- initialize special element properties -------------------------
3086
3087   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3088   if (use_amoeba_dropping_cannot_fall_bug)
3089     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3090
3091   // ---------- initialize player's initial move delay ------------------------
3092
3093   // dynamically adjust player properties according to level information
3094   for (i = 0; i < MAX_PLAYERS; i++)
3095     game.initial_move_delay_value[i] =
3096       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3097
3098   // dynamically adjust player properties according to game engine version
3099   for (i = 0; i < MAX_PLAYERS; i++)
3100     game.initial_move_delay[i] =
3101       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3102        game.initial_move_delay_value[i] : 0);
3103
3104   // ---------- initialize player's initial push delay ------------------------
3105
3106   // dynamically adjust player properties according to game engine version
3107   game.initial_push_delay_value =
3108     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3109
3110   // ---------- initialize changing elements ----------------------------------
3111
3112   // initialize changing elements information
3113   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3114   {
3115     struct ElementInfo *ei = &element_info[i];
3116
3117     // this pointer might have been changed in the level editor
3118     ei->change = &ei->change_page[0];
3119
3120     if (!IS_CUSTOM_ELEMENT(i))
3121     {
3122       ei->change->target_element = EL_EMPTY_SPACE;
3123       ei->change->delay_fixed = 0;
3124       ei->change->delay_random = 0;
3125       ei->change->delay_frames = 1;
3126     }
3127
3128     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3129     {
3130       ei->has_change_event[j] = FALSE;
3131
3132       ei->event_page_nr[j] = 0;
3133       ei->event_page[j] = &ei->change_page[0];
3134     }
3135   }
3136
3137   // add changing elements from pre-defined list
3138   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3139   {
3140     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3141     struct ElementInfo *ei = &element_info[ch_delay->element];
3142
3143     ei->change->target_element       = ch_delay->target_element;
3144     ei->change->delay_fixed          = ch_delay->change_delay;
3145
3146     ei->change->pre_change_function  = ch_delay->pre_change_function;
3147     ei->change->change_function      = ch_delay->change_function;
3148     ei->change->post_change_function = ch_delay->post_change_function;
3149
3150     ei->change->can_change = TRUE;
3151     ei->change->can_change_or_has_action = TRUE;
3152
3153     ei->has_change_event[CE_DELAY] = TRUE;
3154
3155     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3156     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3157   }
3158
3159   // ---------- initialize internal run-time variables ------------------------
3160
3161   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3162   {
3163     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3164
3165     for (j = 0; j < ei->num_change_pages; j++)
3166     {
3167       ei->change_page[j].can_change_or_has_action =
3168         (ei->change_page[j].can_change |
3169          ei->change_page[j].has_action);
3170     }
3171   }
3172
3173   // add change events from custom element configuration
3174   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3175   {
3176     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3177
3178     for (j = 0; j < ei->num_change_pages; j++)
3179     {
3180       if (!ei->change_page[j].can_change_or_has_action)
3181         continue;
3182
3183       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3184       {
3185         // only add event page for the first page found with this event
3186         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3187         {
3188           ei->has_change_event[k] = TRUE;
3189
3190           ei->event_page_nr[k] = j;
3191           ei->event_page[k] = &ei->change_page[j];
3192         }
3193       }
3194     }
3195   }
3196
3197   // ---------- initialize reference elements in change conditions ------------
3198
3199   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3200   {
3201     int element = EL_CUSTOM_START + i;
3202     struct ElementInfo *ei = &element_info[element];
3203
3204     for (j = 0; j < ei->num_change_pages; j++)
3205     {
3206       int trigger_element = ei->change_page[j].initial_trigger_element;
3207
3208       if (trigger_element >= EL_PREV_CE_8 &&
3209           trigger_element <= EL_NEXT_CE_8)
3210         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3211
3212       ei->change_page[j].trigger_element = trigger_element;
3213     }
3214   }
3215
3216   // ---------- initialize run-time trigger player and element ----------------
3217
3218   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3219   {
3220     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3221
3222     for (j = 0; j < ei->num_change_pages; j++)
3223     {
3224       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3225       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3226       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3227       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3228       ei->change_page[j].actual_trigger_ce_value = 0;
3229       ei->change_page[j].actual_trigger_ce_score = 0;
3230     }
3231   }
3232
3233   // ---------- initialize trigger events -------------------------------------
3234
3235   // initialize trigger events information
3236   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3237     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3238       trigger_events[i][j] = FALSE;
3239
3240   // add trigger events from element change event properties
3241   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3242   {
3243     struct ElementInfo *ei = &element_info[i];
3244
3245     for (j = 0; j < ei->num_change_pages; j++)
3246     {
3247       if (!ei->change_page[j].can_change_or_has_action)
3248         continue;
3249
3250       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3251       {
3252         int trigger_element = ei->change_page[j].trigger_element;
3253
3254         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3255         {
3256           if (ei->change_page[j].has_event[k])
3257           {
3258             if (IS_GROUP_ELEMENT(trigger_element))
3259             {
3260               struct ElementGroupInfo *group =
3261                 element_info[trigger_element].group;
3262
3263               for (l = 0; l < group->num_elements_resolved; l++)
3264                 trigger_events[group->element_resolved[l]][k] = TRUE;
3265             }
3266             else if (trigger_element == EL_ANY_ELEMENT)
3267               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3268                 trigger_events[l][k] = TRUE;
3269             else
3270               trigger_events[trigger_element][k] = TRUE;
3271           }
3272         }
3273       }
3274     }
3275   }
3276
3277   // ---------- initialize push delay -----------------------------------------
3278
3279   // initialize push delay values to default
3280   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3281   {
3282     if (!IS_CUSTOM_ELEMENT(i))
3283     {
3284       // set default push delay values (corrected since version 3.0.7-1)
3285       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3286       {
3287         element_info[i].push_delay_fixed = 2;
3288         element_info[i].push_delay_random = 8;
3289       }
3290       else
3291       {
3292         element_info[i].push_delay_fixed = 8;
3293         element_info[i].push_delay_random = 8;
3294       }
3295     }
3296   }
3297
3298   // set push delay value for certain elements from pre-defined list
3299   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3300   {
3301     int e = push_delay_list[i].element;
3302
3303     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3304     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3305   }
3306
3307   // set push delay value for Supaplex elements for newer engine versions
3308   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3309   {
3310     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3311     {
3312       if (IS_SP_ELEMENT(i))
3313       {
3314         // set SP push delay to just enough to push under a falling zonk
3315         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3316
3317         element_info[i].push_delay_fixed  = delay;
3318         element_info[i].push_delay_random = 0;
3319       }
3320     }
3321   }
3322
3323   // ---------- initialize move stepsize --------------------------------------
3324
3325   // initialize move stepsize values to default
3326   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3327     if (!IS_CUSTOM_ELEMENT(i))
3328       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3329
3330   // set move stepsize value for certain elements from pre-defined list
3331   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3332   {
3333     int e = move_stepsize_list[i].element;
3334
3335     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3336
3337     // set move stepsize value for certain elements for older engine versions
3338     if (use_old_move_stepsize_for_magic_wall)
3339     {
3340       if (e == EL_MAGIC_WALL_FILLING ||
3341           e == EL_MAGIC_WALL_EMPTYING ||
3342           e == EL_BD_MAGIC_WALL_FILLING ||
3343           e == EL_BD_MAGIC_WALL_EMPTYING)
3344         element_info[e].move_stepsize *= 2;
3345     }
3346   }
3347
3348   // ---------- initialize collect score --------------------------------------
3349
3350   // initialize collect score values for custom elements from initial value
3351   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3352     if (IS_CUSTOM_ELEMENT(i))
3353       element_info[i].collect_score = element_info[i].collect_score_initial;
3354
3355   // ---------- initialize collect count --------------------------------------
3356
3357   // initialize collect count values for non-custom elements
3358   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3359     if (!IS_CUSTOM_ELEMENT(i))
3360       element_info[i].collect_count_initial = 0;
3361
3362   // add collect count values for all elements from pre-defined list
3363   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3364     element_info[collect_count_list[i].element].collect_count_initial =
3365       collect_count_list[i].count;
3366
3367   // ---------- initialize access direction -----------------------------------
3368
3369   // initialize access direction values to default (access from every side)
3370   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3371     if (!IS_CUSTOM_ELEMENT(i))
3372       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3373
3374   // set access direction value for certain elements from pre-defined list
3375   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3376     element_info[access_direction_list[i].element].access_direction =
3377       access_direction_list[i].direction;
3378
3379   // ---------- initialize explosion content ----------------------------------
3380   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3381   {
3382     if (IS_CUSTOM_ELEMENT(i))
3383       continue;
3384
3385     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3386     {
3387       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3388
3389       element_info[i].content.e[x][y] =
3390         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3391          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3392          i == EL_PLAYER_3 ? EL_EMERALD :
3393          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3394          i == EL_MOLE ? EL_EMERALD_RED :
3395          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3396          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3397          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3398          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3399          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3400          i == EL_WALL_EMERALD ? EL_EMERALD :
3401          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3402          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3403          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3404          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3405          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3406          i == EL_WALL_PEARL ? EL_PEARL :
3407          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3408          EL_EMPTY);
3409     }
3410   }
3411
3412   // ---------- initialize recursion detection --------------------------------
3413   recursion_loop_depth = 0;
3414   recursion_loop_detected = FALSE;
3415   recursion_loop_element = EL_UNDEFINED;
3416
3417   // ---------- initialize graphics engine ------------------------------------
3418   game.scroll_delay_value =
3419     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3420      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3421      !setup.forced_scroll_delay           ? 0 :
3422      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3423   game.scroll_delay_value =
3424     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3425
3426   // ---------- initialize game engine snapshots ------------------------------
3427   for (i = 0; i < MAX_PLAYERS; i++)
3428     game.snapshot.last_action[i] = 0;
3429   game.snapshot.changed_action = FALSE;
3430   game.snapshot.collected_item = FALSE;
3431   game.snapshot.mode =
3432     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3433      SNAPSHOT_MODE_EVERY_STEP :
3434      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3435      SNAPSHOT_MODE_EVERY_MOVE :
3436      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3437      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3438   game.snapshot.save_snapshot = FALSE;
3439
3440   // ---------- initialize level time for Supaplex engine ---------------------
3441   // Supaplex levels with time limit currently unsupported -- should be added
3442   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3443     level.time = 0;
3444
3445   // ---------- initialize flags for handling game actions --------------------
3446
3447   // set flags for game actions to default values
3448   game.use_key_actions = TRUE;
3449   game.use_mouse_actions = FALSE;
3450
3451   // when using Mirror Magic game engine, handle mouse events only
3452   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3453   {
3454     game.use_key_actions = FALSE;
3455     game.use_mouse_actions = TRUE;
3456   }
3457
3458   // check for custom elements with mouse click events
3459   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3460   {
3461     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3462     {
3463       int element = EL_CUSTOM_START + i;
3464
3465       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3466           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3467           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3468           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3469         game.use_mouse_actions = TRUE;
3470     }
3471   }
3472 }
3473
3474 static int get_num_special_action(int element, int action_first,
3475                                   int action_last)
3476 {
3477   int num_special_action = 0;
3478   int i, j;
3479
3480   for (i = action_first; i <= action_last; i++)
3481   {
3482     boolean found = FALSE;
3483
3484     for (j = 0; j < NUM_DIRECTIONS; j++)
3485       if (el_act_dir2img(element, i, j) !=
3486           el_act_dir2img(element, ACTION_DEFAULT, j))
3487         found = TRUE;
3488
3489     if (found)
3490       num_special_action++;
3491     else
3492       break;
3493   }
3494
3495   return num_special_action;
3496 }
3497
3498
3499 // ============================================================================
3500 // InitGame()
3501 // ----------------------------------------------------------------------------
3502 // initialize and start new game
3503 // ============================================================================
3504
3505 #if DEBUG_INIT_PLAYER
3506 static void DebugPrintPlayerStatus(char *message)
3507 {
3508   int i;
3509
3510   if (!options.debug)
3511     return;
3512
3513   Debug("game:init:player", "%s:", message);
3514
3515   for (i = 0; i < MAX_PLAYERS; i++)
3516   {
3517     struct PlayerInfo *player = &stored_player[i];
3518
3519     Debug("game:init:player",
3520           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3521           i + 1,
3522           player->present,
3523           player->connected,
3524           player->connected_locally,
3525           player->connected_network,
3526           player->active,
3527           (local_player == player ? " (local player)" : ""));
3528   }
3529 }
3530 #endif
3531
3532 void InitGame(void)
3533 {
3534   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3535   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3536   int fade_mask = REDRAW_FIELD;
3537
3538   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3539   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3540   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3541   int initial_move_dir = MV_DOWN;
3542   int i, j, x, y;
3543
3544   // required here to update video display before fading (FIX THIS)
3545   DrawMaskedBorder(REDRAW_DOOR_2);
3546
3547   if (!game.restart_level)
3548     CloseDoor(DOOR_CLOSE_1);
3549
3550   SetGameStatus(GAME_MODE_PLAYING);
3551
3552   if (level_editor_test_game)
3553     FadeSkipNextFadeOut();
3554   else
3555     FadeSetEnterScreen();
3556
3557   if (CheckFadeAll())
3558     fade_mask = REDRAW_ALL;
3559
3560   FadeLevelSoundsAndMusic();
3561
3562   ExpireSoundLoops(TRUE);
3563
3564   FadeOut(fade_mask);
3565
3566   if (level_editor_test_game)
3567     FadeSkipNextFadeIn();
3568
3569   // needed if different viewport properties defined for playing
3570   ChangeViewportPropertiesIfNeeded();
3571
3572   ClearField();
3573
3574   DrawCompleteVideoDisplay();
3575
3576   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3577
3578   InitGameEngine();
3579   InitGameControlValues();
3580
3581   if (tape.recording)
3582   {
3583     // initialize tape actions from game when recording tape
3584     tape.use_key_actions   = game.use_key_actions;
3585     tape.use_mouse_actions = game.use_mouse_actions;
3586
3587     // initialize visible playfield size when recording tape (for team mode)
3588     tape.scr_fieldx = SCR_FIELDX;
3589     tape.scr_fieldy = SCR_FIELDY;
3590   }
3591
3592   // don't play tapes over network
3593   network_playing = (network.enabled && !tape.playing);
3594
3595   for (i = 0; i < MAX_PLAYERS; i++)
3596   {
3597     struct PlayerInfo *player = &stored_player[i];
3598
3599     player->index_nr = i;
3600     player->index_bit = (1 << i);
3601     player->element_nr = EL_PLAYER_1 + i;
3602
3603     player->present = FALSE;
3604     player->active = FALSE;
3605     player->mapped = FALSE;
3606
3607     player->killed = FALSE;
3608     player->reanimated = FALSE;
3609     player->buried = FALSE;
3610
3611     player->action = 0;
3612     player->effective_action = 0;
3613     player->programmed_action = 0;
3614     player->snap_action = 0;
3615
3616     player->mouse_action.lx = 0;
3617     player->mouse_action.ly = 0;
3618     player->mouse_action.button = 0;
3619     player->mouse_action.button_hint = 0;
3620
3621     player->effective_mouse_action.lx = 0;
3622     player->effective_mouse_action.ly = 0;
3623     player->effective_mouse_action.button = 0;
3624     player->effective_mouse_action.button_hint = 0;
3625
3626     for (j = 0; j < MAX_NUM_KEYS; j++)
3627       player->key[j] = FALSE;
3628
3629     player->num_white_keys = 0;
3630
3631     player->dynabomb_count = 0;
3632     player->dynabomb_size = 1;
3633     player->dynabombs_left = 0;
3634     player->dynabomb_xl = FALSE;
3635
3636     player->MovDir = initial_move_dir;
3637     player->MovPos = 0;
3638     player->GfxPos = 0;
3639     player->GfxDir = initial_move_dir;
3640     player->GfxAction = ACTION_DEFAULT;
3641     player->Frame = 0;
3642     player->StepFrame = 0;
3643
3644     player->initial_element = player->element_nr;
3645     player->artwork_element =
3646       (level.use_artwork_element[i] ? level.artwork_element[i] :
3647        player->element_nr);
3648     player->use_murphy = FALSE;
3649
3650     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3651     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3652
3653     player->gravity = level.initial_player_gravity[i];
3654
3655     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3656
3657     player->actual_frame_counter = 0;
3658
3659     player->step_counter = 0;
3660
3661     player->last_move_dir = initial_move_dir;
3662
3663     player->is_active = FALSE;
3664
3665     player->is_waiting = FALSE;
3666     player->is_moving = FALSE;
3667     player->is_auto_moving = FALSE;
3668     player->is_digging = FALSE;
3669     player->is_snapping = FALSE;
3670     player->is_collecting = FALSE;
3671     player->is_pushing = FALSE;
3672     player->is_switching = FALSE;
3673     player->is_dropping = FALSE;
3674     player->is_dropping_pressed = FALSE;
3675
3676     player->is_bored = FALSE;
3677     player->is_sleeping = FALSE;
3678
3679     player->was_waiting = TRUE;
3680     player->was_moving = FALSE;
3681     player->was_snapping = FALSE;
3682     player->was_dropping = FALSE;
3683
3684     player->force_dropping = FALSE;
3685
3686     player->frame_counter_bored = -1;
3687     player->frame_counter_sleeping = -1;
3688
3689     player->anim_delay_counter = 0;
3690     player->post_delay_counter = 0;
3691
3692     player->dir_waiting = initial_move_dir;
3693     player->action_waiting = ACTION_DEFAULT;
3694     player->last_action_waiting = ACTION_DEFAULT;
3695     player->special_action_bored = ACTION_DEFAULT;
3696     player->special_action_sleeping = ACTION_DEFAULT;
3697
3698     player->switch_x = -1;
3699     player->switch_y = -1;
3700
3701     player->drop_x = -1;
3702     player->drop_y = -1;
3703
3704     player->show_envelope = 0;
3705
3706     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3707
3708     player->push_delay       = -1;      // initialized when pushing starts
3709     player->push_delay_value = game.initial_push_delay_value;
3710
3711     player->drop_delay = 0;
3712     player->drop_pressed_delay = 0;
3713
3714     player->last_jx = -1;
3715     player->last_jy = -1;
3716     player->jx = -1;
3717     player->jy = -1;
3718
3719     player->shield_normal_time_left = 0;
3720     player->shield_deadly_time_left = 0;
3721
3722     player->last_removed_element = EL_UNDEFINED;
3723
3724     player->inventory_infinite_element = EL_UNDEFINED;
3725     player->inventory_size = 0;
3726
3727     if (level.use_initial_inventory[i])
3728     {
3729       for (j = 0; j < level.initial_inventory_size[i]; j++)
3730       {
3731         int element = level.initial_inventory_content[i][j];
3732         int collect_count = element_info[element].collect_count_initial;
3733         int k;
3734
3735         if (!IS_CUSTOM_ELEMENT(element))
3736           collect_count = 1;
3737
3738         if (collect_count == 0)
3739           player->inventory_infinite_element = element;
3740         else
3741           for (k = 0; k < collect_count; k++)
3742             if (player->inventory_size < MAX_INVENTORY_SIZE)
3743               player->inventory_element[player->inventory_size++] = element;
3744       }
3745     }
3746
3747     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3748     SnapField(player, 0, 0);
3749
3750     map_player_action[i] = i;
3751   }
3752
3753   network_player_action_received = FALSE;
3754
3755   // initial null action
3756   if (network_playing)
3757     SendToServer_MovePlayer(MV_NONE);
3758
3759   FrameCounter = 0;
3760   TimeFrames = 0;
3761   TimePlayed = 0;
3762   TimeLeft = level.time;
3763   TapeTime = 0;
3764
3765   ScreenMovDir = MV_NONE;
3766   ScreenMovPos = 0;
3767   ScreenGfxPos = 0;
3768
3769   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3770
3771   game.robot_wheel_x = -1;
3772   game.robot_wheel_y = -1;
3773
3774   game.exit_x = -1;
3775   game.exit_y = -1;
3776
3777   game.all_players_gone = FALSE;
3778
3779   game.LevelSolved = FALSE;
3780   game.GameOver = FALSE;
3781
3782   game.GamePlayed = !tape.playing;
3783
3784   game.LevelSolved_GameWon = FALSE;
3785   game.LevelSolved_GameEnd = FALSE;
3786   game.LevelSolved_SaveTape = FALSE;
3787   game.LevelSolved_SaveScore = FALSE;
3788
3789   game.LevelSolved_CountingTime = 0;
3790   game.LevelSolved_CountingScore = 0;
3791   game.LevelSolved_CountingHealth = 0;
3792
3793   game.panel.active = TRUE;
3794
3795   game.no_time_limit = (level.time == 0);
3796
3797   game.yamyam_content_nr = 0;
3798   game.robot_wheel_active = FALSE;
3799   game.magic_wall_active = FALSE;
3800   game.magic_wall_time_left = 0;
3801   game.light_time_left = 0;
3802   game.timegate_time_left = 0;
3803   game.switchgate_pos = 0;
3804   game.wind_direction = level.wind_direction_initial;
3805
3806   game.score = 0;
3807   game.score_final = 0;
3808
3809   game.health = MAX_HEALTH;
3810   game.health_final = MAX_HEALTH;
3811
3812   game.gems_still_needed = level.gems_needed;
3813   game.sokoban_fields_still_needed = 0;
3814   game.sokoban_objects_still_needed = 0;
3815   game.lights_still_needed = 0;
3816   game.players_still_needed = 0;
3817   game.friends_still_needed = 0;
3818
3819   game.lenses_time_left = 0;
3820   game.magnify_time_left = 0;
3821
3822   game.ball_active = level.ball_active_initial;
3823   game.ball_content_nr = 0;
3824
3825   game.explosions_delayed = TRUE;
3826
3827   game.envelope_active = FALSE;
3828
3829   for (i = 0; i < NUM_BELTS; i++)
3830   {
3831     game.belt_dir[i] = MV_NONE;
3832     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3833   }
3834
3835   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3836     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3837
3838 #if DEBUG_INIT_PLAYER
3839   DebugPrintPlayerStatus("Player status at level initialization");
3840 #endif
3841
3842   SCAN_PLAYFIELD(x, y)
3843   {
3844     Tile[x][y] = Last[x][y] = level.field[x][y];
3845     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3846     ChangeDelay[x][y] = 0;
3847     ChangePage[x][y] = -1;
3848     CustomValue[x][y] = 0;              // initialized in InitField()
3849     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3850     AmoebaNr[x][y] = 0;
3851     WasJustMoving[x][y] = 0;
3852     WasJustFalling[x][y] = 0;
3853     CheckCollision[x][y] = 0;
3854     CheckImpact[x][y] = 0;
3855     Stop[x][y] = FALSE;
3856     Pushed[x][y] = FALSE;
3857
3858     ChangeCount[x][y] = 0;
3859     ChangeEvent[x][y] = -1;
3860
3861     ExplodePhase[x][y] = 0;
3862     ExplodeDelay[x][y] = 0;
3863     ExplodeField[x][y] = EX_TYPE_NONE;
3864
3865     RunnerVisit[x][y] = 0;
3866     PlayerVisit[x][y] = 0;
3867
3868     GfxFrame[x][y] = 0;
3869     GfxRandom[x][y] = INIT_GFX_RANDOM();
3870     GfxElement[x][y] = EL_UNDEFINED;
3871     GfxAction[x][y] = ACTION_DEFAULT;
3872     GfxDir[x][y] = MV_NONE;
3873     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3874   }
3875
3876   SCAN_PLAYFIELD(x, y)
3877   {
3878     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3879       emulate_bd = FALSE;
3880     if (emulate_sb && !IS_SB_ELEMENT(Tile[x][y]))
3881       emulate_sb = FALSE;
3882     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3883       emulate_sp = FALSE;
3884
3885     InitField(x, y, TRUE);
3886
3887     ResetGfxAnimation(x, y);
3888   }
3889
3890   InitBeltMovement();
3891
3892   for (i = 0; i < MAX_PLAYERS; i++)
3893   {
3894     struct PlayerInfo *player = &stored_player[i];
3895
3896     // set number of special actions for bored and sleeping animation
3897     player->num_special_action_bored =
3898       get_num_special_action(player->artwork_element,
3899                              ACTION_BORING_1, ACTION_BORING_LAST);
3900     player->num_special_action_sleeping =
3901       get_num_special_action(player->artwork_element,
3902                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3903   }
3904
3905   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3906                     emulate_sb ? EMU_SOKOBAN :
3907                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3908
3909   // initialize type of slippery elements
3910   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3911   {
3912     if (!IS_CUSTOM_ELEMENT(i))
3913     {
3914       // default: elements slip down either to the left or right randomly
3915       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3916
3917       // SP style elements prefer to slip down on the left side
3918       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3919         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3920
3921       // BD style elements prefer to slip down on the left side
3922       if (game.emulation == EMU_BOULDERDASH)
3923         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3924     }
3925   }
3926
3927   // initialize explosion and ignition delay
3928   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3929   {
3930     if (!IS_CUSTOM_ELEMENT(i))
3931     {
3932       int num_phase = 8;
3933       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3934                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3935                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3936       int last_phase = (num_phase + 1) * delay;
3937       int half_phase = (num_phase / 2) * delay;
3938
3939       element_info[i].explosion_delay = last_phase - 1;
3940       element_info[i].ignition_delay = half_phase;
3941
3942       if (i == EL_BLACK_ORB)
3943         element_info[i].ignition_delay = 1;
3944     }
3945   }
3946
3947   // correct non-moving belts to start moving left
3948   for (i = 0; i < NUM_BELTS; i++)
3949     if (game.belt_dir[i] == MV_NONE)
3950       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3951
3952 #if USE_NEW_PLAYER_ASSIGNMENTS
3953   // use preferred player also in local single-player mode
3954   if (!network.enabled && !game.team_mode)
3955   {
3956     int new_index_nr = setup.network_player_nr;
3957
3958     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3959     {
3960       for (i = 0; i < MAX_PLAYERS; i++)
3961         stored_player[i].connected_locally = FALSE;
3962
3963       stored_player[new_index_nr].connected_locally = TRUE;
3964     }
3965   }
3966
3967   for (i = 0; i < MAX_PLAYERS; i++)
3968   {
3969     stored_player[i].connected = FALSE;
3970
3971     // in network game mode, the local player might not be the first player
3972     if (stored_player[i].connected_locally)
3973       local_player = &stored_player[i];
3974   }
3975
3976   if (!network.enabled)
3977     local_player->connected = TRUE;
3978
3979   if (tape.playing)
3980   {
3981     for (i = 0; i < MAX_PLAYERS; i++)
3982       stored_player[i].connected = tape.player_participates[i];
3983   }
3984   else if (network.enabled)
3985   {
3986     // add team mode players connected over the network (needed for correct
3987     // assignment of player figures from level to locally playing players)
3988
3989     for (i = 0; i < MAX_PLAYERS; i++)
3990       if (stored_player[i].connected_network)
3991         stored_player[i].connected = TRUE;
3992   }
3993   else if (game.team_mode)
3994   {
3995     // try to guess locally connected team mode players (needed for correct
3996     // assignment of player figures from level to locally playing players)
3997
3998     for (i = 0; i < MAX_PLAYERS; i++)
3999       if (setup.input[i].use_joystick ||
4000           setup.input[i].key.left != KSYM_UNDEFINED)
4001         stored_player[i].connected = TRUE;
4002   }
4003
4004 #if DEBUG_INIT_PLAYER
4005   DebugPrintPlayerStatus("Player status after level initialization");
4006 #endif
4007
4008 #if DEBUG_INIT_PLAYER
4009   Debug("game:init:player", "Reassigning players ...");
4010 #endif
4011
4012   // check if any connected player was not found in playfield
4013   for (i = 0; i < MAX_PLAYERS; i++)
4014   {
4015     struct PlayerInfo *player = &stored_player[i];
4016
4017     if (player->connected && !player->present)
4018     {
4019       struct PlayerInfo *field_player = NULL;
4020
4021 #if DEBUG_INIT_PLAYER
4022       Debug("game:init:player",
4023             "- looking for field player for player %d ...", i + 1);
4024 #endif
4025
4026       // assign first free player found that is present in the playfield
4027
4028       // first try: look for unmapped playfield player that is not connected
4029       for (j = 0; j < MAX_PLAYERS; j++)
4030         if (field_player == NULL &&
4031             stored_player[j].present &&
4032             !stored_player[j].mapped &&
4033             !stored_player[j].connected)
4034           field_player = &stored_player[j];
4035
4036       // second try: look for *any* unmapped playfield player
4037       for (j = 0; j < MAX_PLAYERS; j++)
4038         if (field_player == NULL &&
4039             stored_player[j].present &&
4040             !stored_player[j].mapped)
4041           field_player = &stored_player[j];
4042
4043       if (field_player != NULL)
4044       {
4045         int jx = field_player->jx, jy = field_player->jy;
4046
4047 #if DEBUG_INIT_PLAYER
4048         Debug("game:init:player", "- found player %d",
4049               field_player->index_nr + 1);
4050 #endif
4051
4052         player->present = FALSE;
4053         player->active = FALSE;
4054
4055         field_player->present = TRUE;
4056         field_player->active = TRUE;
4057
4058         /*
4059         player->initial_element = field_player->initial_element;
4060         player->artwork_element = field_player->artwork_element;
4061
4062         player->block_last_field       = field_player->block_last_field;
4063         player->block_delay_adjustment = field_player->block_delay_adjustment;
4064         */
4065
4066         StorePlayer[jx][jy] = field_player->element_nr;
4067
4068         field_player->jx = field_player->last_jx = jx;
4069         field_player->jy = field_player->last_jy = jy;
4070
4071         if (local_player == player)
4072           local_player = field_player;
4073
4074         map_player_action[field_player->index_nr] = i;
4075
4076         field_player->mapped = TRUE;
4077
4078 #if DEBUG_INIT_PLAYER
4079         Debug("game:init:player", "- map_player_action[%d] == %d",
4080               field_player->index_nr + 1, i + 1);
4081 #endif
4082       }
4083     }
4084
4085     if (player->connected && player->present)
4086       player->mapped = TRUE;
4087   }
4088
4089 #if DEBUG_INIT_PLAYER
4090   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4091 #endif
4092
4093 #else
4094
4095   // check if any connected player was not found in playfield
4096   for (i = 0; i < MAX_PLAYERS; i++)
4097   {
4098     struct PlayerInfo *player = &stored_player[i];
4099
4100     if (player->connected && !player->present)
4101     {
4102       for (j = 0; j < MAX_PLAYERS; j++)
4103       {
4104         struct PlayerInfo *field_player = &stored_player[j];
4105         int jx = field_player->jx, jy = field_player->jy;
4106
4107         // assign first free player found that is present in the playfield
4108         if (field_player->present && !field_player->connected)
4109         {
4110           player->present = TRUE;
4111           player->active = TRUE;
4112
4113           field_player->present = FALSE;
4114           field_player->active = FALSE;
4115
4116           player->initial_element = field_player->initial_element;
4117           player->artwork_element = field_player->artwork_element;
4118
4119           player->block_last_field       = field_player->block_last_field;
4120           player->block_delay_adjustment = field_player->block_delay_adjustment;
4121
4122           StorePlayer[jx][jy] = player->element_nr;
4123
4124           player->jx = player->last_jx = jx;
4125           player->jy = player->last_jy = jy;
4126
4127           break;
4128         }
4129       }
4130     }
4131   }
4132 #endif
4133
4134 #if 0
4135   Debug("game:init:player", "local_player->present == %d",
4136         local_player->present);
4137 #endif
4138
4139   // set focus to local player for network games, else to all players
4140   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4141   game.centered_player_nr_next = game.centered_player_nr;
4142   game.set_centered_player = FALSE;
4143   game.set_centered_player_wrap = FALSE;
4144
4145   if (network_playing && tape.recording)
4146   {
4147     // store client dependent player focus when recording network games
4148     tape.centered_player_nr_next = game.centered_player_nr_next;
4149     tape.set_centered_player = TRUE;
4150   }
4151
4152   if (tape.playing)
4153   {
4154     // when playing a tape, eliminate all players who do not participate
4155
4156 #if USE_NEW_PLAYER_ASSIGNMENTS
4157
4158     if (!game.team_mode)
4159     {
4160       for (i = 0; i < MAX_PLAYERS; i++)
4161       {
4162         if (stored_player[i].active &&
4163             !tape.player_participates[map_player_action[i]])
4164         {
4165           struct PlayerInfo *player = &stored_player[i];
4166           int jx = player->jx, jy = player->jy;
4167
4168 #if DEBUG_INIT_PLAYER
4169           Debug("game:init:player", "Removing player %d at (%d, %d)",
4170                 i + 1, jx, jy);
4171 #endif
4172
4173           player->active = FALSE;
4174           StorePlayer[jx][jy] = 0;
4175           Tile[jx][jy] = EL_EMPTY;
4176         }
4177       }
4178     }
4179
4180 #else
4181
4182     for (i = 0; i < MAX_PLAYERS; i++)
4183     {
4184       if (stored_player[i].active &&
4185           !tape.player_participates[i])
4186       {
4187         struct PlayerInfo *player = &stored_player[i];
4188         int jx = player->jx, jy = player->jy;
4189
4190         player->active = FALSE;
4191         StorePlayer[jx][jy] = 0;
4192         Tile[jx][jy] = EL_EMPTY;
4193       }
4194     }
4195 #endif
4196   }
4197   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4198   {
4199     // when in single player mode, eliminate all but the local player
4200
4201     for (i = 0; i < MAX_PLAYERS; i++)
4202     {
4203       struct PlayerInfo *player = &stored_player[i];
4204
4205       if (player->active && player != local_player)
4206       {
4207         int jx = player->jx, jy = player->jy;
4208
4209         player->active = FALSE;
4210         player->present = FALSE;
4211
4212         StorePlayer[jx][jy] = 0;
4213         Tile[jx][jy] = EL_EMPTY;
4214       }
4215     }
4216   }
4217
4218   for (i = 0; i < MAX_PLAYERS; i++)
4219     if (stored_player[i].active)
4220       game.players_still_needed++;
4221
4222   if (level.solved_by_one_player)
4223     game.players_still_needed = 1;
4224
4225   // when recording the game, store which players take part in the game
4226   if (tape.recording)
4227   {
4228 #if USE_NEW_PLAYER_ASSIGNMENTS
4229     for (i = 0; i < MAX_PLAYERS; i++)
4230       if (stored_player[i].connected)
4231         tape.player_participates[i] = TRUE;
4232 #else
4233     for (i = 0; i < MAX_PLAYERS; i++)
4234       if (stored_player[i].active)
4235         tape.player_participates[i] = TRUE;
4236 #endif
4237   }
4238
4239 #if DEBUG_INIT_PLAYER
4240   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4241 #endif
4242
4243   if (BorderElement == EL_EMPTY)
4244   {
4245     SBX_Left = 0;
4246     SBX_Right = lev_fieldx - SCR_FIELDX;
4247     SBY_Upper = 0;
4248     SBY_Lower = lev_fieldy - SCR_FIELDY;
4249   }
4250   else
4251   {
4252     SBX_Left = -1;
4253     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4254     SBY_Upper = -1;
4255     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4256   }
4257
4258   if (full_lev_fieldx <= SCR_FIELDX)
4259     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4260   if (full_lev_fieldy <= SCR_FIELDY)
4261     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4262
4263   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4264     SBX_Left--;
4265   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4266     SBY_Upper--;
4267
4268   // if local player not found, look for custom element that might create
4269   // the player (make some assumptions about the right custom element)
4270   if (!local_player->present)
4271   {
4272     int start_x = 0, start_y = 0;
4273     int found_rating = 0;
4274     int found_element = EL_UNDEFINED;
4275     int player_nr = local_player->index_nr;
4276
4277     SCAN_PLAYFIELD(x, y)
4278     {
4279       int element = Tile[x][y];
4280       int content;
4281       int xx, yy;
4282       boolean is_player;
4283
4284       if (level.use_start_element[player_nr] &&
4285           level.start_element[player_nr] == element &&
4286           found_rating < 4)
4287       {
4288         start_x = x;
4289         start_y = y;
4290
4291         found_rating = 4;
4292         found_element = element;
4293       }
4294
4295       if (!IS_CUSTOM_ELEMENT(element))
4296         continue;
4297
4298       if (CAN_CHANGE(element))
4299       {
4300         for (i = 0; i < element_info[element].num_change_pages; i++)
4301         {
4302           // check for player created from custom element as single target
4303           content = element_info[element].change_page[i].target_element;
4304           is_player = ELEM_IS_PLAYER(content);
4305
4306           if (is_player && (found_rating < 3 ||
4307                             (found_rating == 3 && element < found_element)))
4308           {
4309             start_x = x;
4310             start_y = y;
4311
4312             found_rating = 3;
4313             found_element = element;
4314           }
4315         }
4316       }
4317
4318       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4319       {
4320         // check for player created from custom element as explosion content
4321         content = element_info[element].content.e[xx][yy];
4322         is_player = ELEM_IS_PLAYER(content);
4323
4324         if (is_player && (found_rating < 2 ||
4325                           (found_rating == 2 && element < found_element)))
4326         {
4327           start_x = x + xx - 1;
4328           start_y = y + yy - 1;
4329
4330           found_rating = 2;
4331           found_element = element;
4332         }
4333
4334         if (!CAN_CHANGE(element))
4335           continue;
4336
4337         for (i = 0; i < element_info[element].num_change_pages; i++)
4338         {
4339           // check for player created from custom element as extended target
4340           content =
4341             element_info[element].change_page[i].target_content.e[xx][yy];
4342
4343           is_player = ELEM_IS_PLAYER(content);
4344
4345           if (is_player && (found_rating < 1 ||
4346                             (found_rating == 1 && element < found_element)))
4347           {
4348             start_x = x + xx - 1;
4349             start_y = y + yy - 1;
4350
4351             found_rating = 1;
4352             found_element = element;
4353           }
4354         }
4355       }
4356     }
4357
4358     scroll_x = SCROLL_POSITION_X(start_x);
4359     scroll_y = SCROLL_POSITION_Y(start_y);
4360   }
4361   else
4362   {
4363     scroll_x = SCROLL_POSITION_X(local_player->jx);
4364     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4365   }
4366
4367   // !!! FIX THIS (START) !!!
4368   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4369   {
4370     InitGameEngine_EM();
4371   }
4372   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4373   {
4374     InitGameEngine_SP();
4375   }
4376   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4377   {
4378     InitGameEngine_MM();
4379   }
4380   else
4381   {
4382     DrawLevel(REDRAW_FIELD);
4383     DrawAllPlayers();
4384
4385     // after drawing the level, correct some elements
4386     if (game.timegate_time_left == 0)
4387       CloseAllOpenTimegates();
4388   }
4389
4390   // blit playfield from scroll buffer to normal back buffer for fading in
4391   BlitScreenToBitmap(backbuffer);
4392   // !!! FIX THIS (END) !!!
4393
4394   DrawMaskedBorder(fade_mask);
4395
4396   FadeIn(fade_mask);
4397
4398 #if 1
4399   // full screen redraw is required at this point in the following cases:
4400   // - special editor door undrawn when game was started from level editor
4401   // - drawing area (playfield) was changed and has to be removed completely
4402   redraw_mask = REDRAW_ALL;
4403   BackToFront();
4404 #endif
4405
4406   if (!game.restart_level)
4407   {
4408     // copy default game door content to main double buffer
4409
4410     // !!! CHECK AGAIN !!!
4411     SetPanelBackground();
4412     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4413     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4414   }
4415
4416   SetPanelBackground();
4417   SetDrawBackgroundMask(REDRAW_DOOR_1);
4418
4419   UpdateAndDisplayGameControlValues();
4420
4421   if (!game.restart_level)
4422   {
4423     UnmapGameButtons();
4424     UnmapTapeButtons();
4425
4426     FreeGameButtons();
4427     CreateGameButtons();
4428
4429     MapGameButtons();
4430     MapTapeButtons();
4431
4432     // copy actual game door content to door double buffer for OpenDoor()
4433     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4434
4435     OpenDoor(DOOR_OPEN_ALL);
4436
4437     KeyboardAutoRepeatOffUnlessAutoplay();
4438
4439 #if DEBUG_INIT_PLAYER
4440     DebugPrintPlayerStatus("Player status (final)");
4441 #endif
4442   }
4443
4444   UnmapAllGadgets();
4445
4446   MapGameButtons();
4447   MapTapeButtons();
4448
4449   if (!game.restart_level && !tape.playing)
4450   {
4451     LevelStats_incPlayed(level_nr);
4452
4453     SaveLevelSetup_SeriesInfo();
4454   }
4455
4456   game.restart_level = FALSE;
4457   game.restart_game_message = NULL;
4458
4459   game.request_active = FALSE;
4460   game.request_active_or_moving = FALSE;
4461
4462   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4463     InitGameActions_MM();
4464
4465   SaveEngineSnapshotToListInitial();
4466
4467   if (!game.restart_level)
4468   {
4469     PlaySound(SND_GAME_STARTING);
4470
4471     if (setup.sound_music)
4472       PlayLevelMusic();
4473   }
4474 }
4475
4476 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4477                         int actual_player_x, int actual_player_y)
4478 {
4479   // this is used for non-R'n'D game engines to update certain engine values
4480
4481   // needed to determine if sounds are played within the visible screen area
4482   scroll_x = actual_scroll_x;
4483   scroll_y = actual_scroll_y;
4484
4485   // needed to get player position for "follow finger" playing input method
4486   local_player->jx = actual_player_x;
4487   local_player->jy = actual_player_y;
4488 }
4489
4490 void InitMovDir(int x, int y)
4491 {
4492   int i, element = Tile[x][y];
4493   static int xy[4][2] =
4494   {
4495     {  0, +1 },
4496     { +1,  0 },
4497     {  0, -1 },
4498     { -1,  0 }
4499   };
4500   static int direction[3][4] =
4501   {
4502     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4503     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4504     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4505   };
4506
4507   switch (element)
4508   {
4509     case EL_BUG_RIGHT:
4510     case EL_BUG_UP:
4511     case EL_BUG_LEFT:
4512     case EL_BUG_DOWN:
4513       Tile[x][y] = EL_BUG;
4514       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4515       break;
4516
4517     case EL_SPACESHIP_RIGHT:
4518     case EL_SPACESHIP_UP:
4519     case EL_SPACESHIP_LEFT:
4520     case EL_SPACESHIP_DOWN:
4521       Tile[x][y] = EL_SPACESHIP;
4522       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4523       break;
4524
4525     case EL_BD_BUTTERFLY_RIGHT:
4526     case EL_BD_BUTTERFLY_UP:
4527     case EL_BD_BUTTERFLY_LEFT:
4528     case EL_BD_BUTTERFLY_DOWN:
4529       Tile[x][y] = EL_BD_BUTTERFLY;
4530       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4531       break;
4532
4533     case EL_BD_FIREFLY_RIGHT:
4534     case EL_BD_FIREFLY_UP:
4535     case EL_BD_FIREFLY_LEFT:
4536     case EL_BD_FIREFLY_DOWN:
4537       Tile[x][y] = EL_BD_FIREFLY;
4538       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4539       break;
4540
4541     case EL_PACMAN_RIGHT:
4542     case EL_PACMAN_UP:
4543     case EL_PACMAN_LEFT:
4544     case EL_PACMAN_DOWN:
4545       Tile[x][y] = EL_PACMAN;
4546       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4547       break;
4548
4549     case EL_YAMYAM_LEFT:
4550     case EL_YAMYAM_RIGHT:
4551     case EL_YAMYAM_UP:
4552     case EL_YAMYAM_DOWN:
4553       Tile[x][y] = EL_YAMYAM;
4554       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4555       break;
4556
4557     case EL_SP_SNIKSNAK:
4558       MovDir[x][y] = MV_UP;
4559       break;
4560
4561     case EL_SP_ELECTRON:
4562       MovDir[x][y] = MV_LEFT;
4563       break;
4564
4565     case EL_MOLE_LEFT:
4566     case EL_MOLE_RIGHT:
4567     case EL_MOLE_UP:
4568     case EL_MOLE_DOWN:
4569       Tile[x][y] = EL_MOLE;
4570       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4571       break;
4572
4573     case EL_SPRING_LEFT:
4574     case EL_SPRING_RIGHT:
4575       Tile[x][y] = EL_SPRING;
4576       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4577       break;
4578
4579     default:
4580       if (IS_CUSTOM_ELEMENT(element))
4581       {
4582         struct ElementInfo *ei = &element_info[element];
4583         int move_direction_initial = ei->move_direction_initial;
4584         int move_pattern = ei->move_pattern;
4585
4586         if (move_direction_initial == MV_START_PREVIOUS)
4587         {
4588           if (MovDir[x][y] != MV_NONE)
4589             return;
4590
4591           move_direction_initial = MV_START_AUTOMATIC;
4592         }
4593
4594         if (move_direction_initial == MV_START_RANDOM)
4595           MovDir[x][y] = 1 << RND(4);
4596         else if (move_direction_initial & MV_ANY_DIRECTION)
4597           MovDir[x][y] = move_direction_initial;
4598         else if (move_pattern == MV_ALL_DIRECTIONS ||
4599                  move_pattern == MV_TURNING_LEFT ||
4600                  move_pattern == MV_TURNING_RIGHT ||
4601                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4602                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4603                  move_pattern == MV_TURNING_RANDOM)
4604           MovDir[x][y] = 1 << RND(4);
4605         else if (move_pattern == MV_HORIZONTAL)
4606           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4607         else if (move_pattern == MV_VERTICAL)
4608           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4609         else if (move_pattern & MV_ANY_DIRECTION)
4610           MovDir[x][y] = element_info[element].move_pattern;
4611         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4612                  move_pattern == MV_ALONG_RIGHT_SIDE)
4613         {
4614           // use random direction as default start direction
4615           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4616             MovDir[x][y] = 1 << RND(4);
4617
4618           for (i = 0; i < NUM_DIRECTIONS; i++)
4619           {
4620             int x1 = x + xy[i][0];
4621             int y1 = y + xy[i][1];
4622
4623             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4624             {
4625               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4626                 MovDir[x][y] = direction[0][i];
4627               else
4628                 MovDir[x][y] = direction[1][i];
4629
4630               break;
4631             }
4632           }
4633         }                
4634       }
4635       else
4636       {
4637         MovDir[x][y] = 1 << RND(4);
4638
4639         if (element != EL_BUG &&
4640             element != EL_SPACESHIP &&
4641             element != EL_BD_BUTTERFLY &&
4642             element != EL_BD_FIREFLY)
4643           break;
4644
4645         for (i = 0; i < NUM_DIRECTIONS; i++)
4646         {
4647           int x1 = x + xy[i][0];
4648           int y1 = y + xy[i][1];
4649
4650           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4651           {
4652             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4653             {
4654               MovDir[x][y] = direction[0][i];
4655               break;
4656             }
4657             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4658                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4659             {
4660               MovDir[x][y] = direction[1][i];
4661               break;
4662             }
4663           }
4664         }
4665       }
4666       break;
4667   }
4668
4669   GfxDir[x][y] = MovDir[x][y];
4670 }
4671
4672 void InitAmoebaNr(int x, int y)
4673 {
4674   int i;
4675   int group_nr = AmoebaNeighbourNr(x, y);
4676
4677   if (group_nr == 0)
4678   {
4679     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4680     {
4681       if (AmoebaCnt[i] == 0)
4682       {
4683         group_nr = i;
4684         break;
4685       }
4686     }
4687   }
4688
4689   AmoebaNr[x][y] = group_nr;
4690   AmoebaCnt[group_nr]++;
4691   AmoebaCnt2[group_nr]++;
4692 }
4693
4694 static void LevelSolved(void)
4695 {
4696   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4697       game.players_still_needed > 0)
4698     return;
4699
4700   game.LevelSolved = TRUE;
4701   game.GameOver = TRUE;
4702
4703   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4704                       game_em.lev->score :
4705                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4706                       game_mm.score :
4707                       game.score);
4708   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4709                        MM_HEALTH(game_mm.laser_overload_value) :
4710                        game.health);
4711
4712   game.LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed : TimeLeft);
4713   game.LevelSolved_CountingScore = game.score_final;
4714   game.LevelSolved_CountingHealth = game.health_final;
4715 }
4716
4717 void GameWon(void)
4718 {
4719   static int time_count_steps;
4720   static int time, time_final;
4721   static int score, score_final;
4722   static int health, health_final;
4723   static int game_over_delay_1 = 0;
4724   static int game_over_delay_2 = 0;
4725   static int game_over_delay_3 = 0;
4726   int game_over_delay_value_1 = 50;
4727   int game_over_delay_value_2 = 25;
4728   int game_over_delay_value_3 = 50;
4729   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4730   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4731
4732   if (!game.LevelSolved_GameWon)
4733   {
4734     int i;
4735
4736     // do not start end game actions before the player stops moving (to exit)
4737     if (local_player->active && local_player->MovPos)
4738       return;
4739
4740     game.LevelSolved_GameWon = TRUE;
4741     game.LevelSolved_SaveTape = tape.recording;
4742     game.LevelSolved_SaveScore = !tape.playing;
4743
4744     if (!tape.playing)
4745     {
4746       LevelStats_incSolved(level_nr);
4747
4748       SaveLevelSetup_SeriesInfo();
4749     }
4750
4751     if (tape.auto_play)         // tape might already be stopped here
4752       tape.auto_play_level_solved = TRUE;
4753
4754     TapeStop();
4755
4756     game_over_delay_1 = 0;
4757     game_over_delay_2 = 0;
4758     game_over_delay_3 = game_over_delay_value_3;
4759
4760     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4761     score = score_final = game.score_final;
4762     health = health_final = game.health_final;
4763
4764     if (time_score > 0)
4765     {
4766       if (TimeLeft > 0)
4767       {
4768         time_final = 0;
4769         score_final += TimeLeft * time_score;
4770       }
4771       else if (game.no_time_limit && TimePlayed < 999)
4772       {
4773         time_final = 999;
4774         score_final += (999 - TimePlayed) * time_score;
4775       }
4776
4777       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4778
4779       game_over_delay_1 = game_over_delay_value_1;
4780
4781       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4782       {
4783         health_final = 0;
4784         score_final += health * time_score;
4785
4786         game_over_delay_2 = game_over_delay_value_2;
4787       }
4788
4789       game.score_final = score_final;
4790       game.health_final = health_final;
4791     }
4792
4793     if (level_editor_test_game)
4794     {
4795       time = time_final;
4796       score = score_final;
4797
4798       game.LevelSolved_CountingTime = time;
4799       game.LevelSolved_CountingScore = score;
4800
4801       game_panel_controls[GAME_PANEL_TIME].value = time;
4802       game_panel_controls[GAME_PANEL_SCORE].value = score;
4803
4804       DisplayGameControlValues();
4805     }
4806
4807     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4808     {
4809       // check if last player has left the level
4810       if (game.exit_x >= 0 &&
4811           game.exit_y >= 0)
4812       {
4813         int x = game.exit_x;
4814         int y = game.exit_y;
4815         int element = Tile[x][y];
4816
4817         // close exit door after last player
4818         if ((game.all_players_gone &&
4819              (element == EL_EXIT_OPEN ||
4820               element == EL_SP_EXIT_OPEN ||
4821               element == EL_STEEL_EXIT_OPEN)) ||
4822             element == EL_EM_EXIT_OPEN ||
4823             element == EL_EM_STEEL_EXIT_OPEN)
4824         {
4825
4826           Tile[x][y] =
4827             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4828              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4829              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4830              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4831              EL_EM_STEEL_EXIT_CLOSING);
4832
4833           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4834         }
4835
4836         // player disappears
4837         DrawLevelField(x, y);
4838       }
4839
4840       for (i = 0; i < MAX_PLAYERS; i++)
4841       {
4842         struct PlayerInfo *player = &stored_player[i];
4843
4844         if (player->present)
4845         {
4846           RemovePlayer(player);
4847
4848           // player disappears
4849           DrawLevelField(player->jx, player->jy);
4850         }
4851       }
4852     }
4853
4854     PlaySound(SND_GAME_WINNING);
4855   }
4856
4857   if (game_over_delay_1 > 0)
4858   {
4859     game_over_delay_1--;
4860
4861     return;
4862   }
4863
4864   if (time != time_final)
4865   {
4866     int time_to_go = ABS(time_final - time);
4867     int time_count_dir = (time < time_final ? +1 : -1);
4868
4869     if (time_to_go < time_count_steps)
4870       time_count_steps = 1;
4871
4872     time  += time_count_steps * time_count_dir;
4873     score += time_count_steps * time_score;
4874
4875     game.LevelSolved_CountingTime = time;
4876     game.LevelSolved_CountingScore = score;
4877
4878     game_panel_controls[GAME_PANEL_TIME].value = time;
4879     game_panel_controls[GAME_PANEL_SCORE].value = score;
4880
4881     DisplayGameControlValues();
4882
4883     if (time == time_final)
4884       StopSound(SND_GAME_LEVELTIME_BONUS);
4885     else if (setup.sound_loops)
4886       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4887     else
4888       PlaySound(SND_GAME_LEVELTIME_BONUS);
4889
4890     return;
4891   }
4892
4893   if (game_over_delay_2 > 0)
4894   {
4895     game_over_delay_2--;
4896
4897     return;
4898   }
4899
4900   if (health != health_final)
4901   {
4902     int health_count_dir = (health < health_final ? +1 : -1);
4903
4904     health += health_count_dir;
4905     score  += time_score;
4906
4907     game.LevelSolved_CountingHealth = health;
4908     game.LevelSolved_CountingScore = score;
4909
4910     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4911     game_panel_controls[GAME_PANEL_SCORE].value = score;
4912
4913     DisplayGameControlValues();
4914
4915     if (health == health_final)
4916       StopSound(SND_GAME_LEVELTIME_BONUS);
4917     else if (setup.sound_loops)
4918       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4919     else
4920       PlaySound(SND_GAME_LEVELTIME_BONUS);
4921
4922     return;
4923   }
4924
4925   game.panel.active = FALSE;
4926
4927   if (game_over_delay_3 > 0)
4928   {
4929     game_over_delay_3--;
4930
4931     return;
4932   }
4933
4934   GameEnd();
4935 }
4936
4937 void GameEnd(void)
4938 {
4939   // used instead of "level_nr" (needed for network games)
4940   int last_level_nr = levelset.level_nr;
4941   int hi_pos;
4942
4943   game.LevelSolved_GameEnd = TRUE;
4944
4945   if (game.LevelSolved_SaveTape)
4946   {
4947     // make sure that request dialog to save tape does not open door again
4948     if (!global.use_envelope_request)
4949       CloseDoor(DOOR_CLOSE_1);
4950
4951     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4952   }
4953
4954   // if no tape is to be saved, close both doors simultaneously
4955   CloseDoor(DOOR_CLOSE_ALL);
4956
4957   if (level_editor_test_game)
4958   {
4959     SetGameStatus(GAME_MODE_MAIN);
4960
4961     DrawMainMenu();
4962
4963     return;
4964   }
4965
4966   if (!game.LevelSolved_SaveScore)
4967   {
4968     SetGameStatus(GAME_MODE_MAIN);
4969
4970     DrawMainMenu();
4971
4972     return;
4973   }
4974
4975   if (level_nr == leveldir_current->handicap_level)
4976   {
4977     leveldir_current->handicap_level++;
4978
4979     SaveLevelSetup_SeriesInfo();
4980   }
4981
4982   if (setup.increment_levels &&
4983       level_nr < leveldir_current->last_level &&
4984       !network_playing)
4985   {
4986     level_nr++;         // advance to next level
4987     TapeErase();        // start with empty tape
4988
4989     if (setup.auto_play_next_level)
4990     {
4991       LoadLevel(level_nr);
4992
4993       SaveLevelSetup_SeriesInfo();
4994     }
4995   }
4996
4997   hi_pos = NewHiScore(last_level_nr);
4998
4999   if (hi_pos >= 0 && !setup.skip_scores_after_game)
5000   {
5001     SetGameStatus(GAME_MODE_SCORES);
5002
5003     DrawHallOfFame(last_level_nr, hi_pos);
5004   }
5005   else if (setup.auto_play_next_level && setup.increment_levels &&
5006            last_level_nr < leveldir_current->last_level &&
5007            !network_playing)
5008   {
5009     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5010   }
5011   else
5012   {
5013     SetGameStatus(GAME_MODE_MAIN);
5014
5015     DrawMainMenu();
5016   }
5017 }
5018
5019 int NewHiScore(int level_nr)
5020 {
5021   int k, l;
5022   int position = -1;
5023   boolean one_score_entry_per_name = !program.many_scores_per_name;
5024
5025   LoadScore(level_nr);
5026
5027   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5028       game.score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
5029     return -1;
5030
5031   for (k = 0; k < MAX_SCORE_ENTRIES; k++)
5032   {
5033     if (game.score_final > highscore[k].Score)
5034     {
5035       // player has made it to the hall of fame
5036
5037       if (k < MAX_SCORE_ENTRIES - 1)
5038       {
5039         int m = MAX_SCORE_ENTRIES - 1;
5040
5041         if (one_score_entry_per_name)
5042         {
5043           for (l = k; l < MAX_SCORE_ENTRIES; l++)
5044             if (strEqual(setup.player_name, highscore[l].Name))
5045               m = l;
5046
5047           if (m == k)   // player's new highscore overwrites his old one
5048             goto put_into_list;
5049         }
5050
5051         for (l = m; l > k; l--)
5052         {
5053           strcpy(highscore[l].Name, highscore[l - 1].Name);
5054           highscore[l].Score = highscore[l - 1].Score;
5055         }
5056       }
5057
5058       put_into_list:
5059
5060       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5061       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5062       highscore[k].Score = game.score_final;
5063       position = k;
5064
5065       break;
5066     }
5067     else if (one_score_entry_per_name &&
5068              !strncmp(setup.player_name, highscore[k].Name,
5069                       MAX_PLAYER_NAME_LEN))
5070       break;    // player already there with a higher score
5071   }
5072
5073   if (position >= 0) 
5074     SaveScore(level_nr);
5075
5076   return position;
5077 }
5078
5079 static int getElementMoveStepsizeExt(int x, int y, int direction)
5080 {
5081   int element = Tile[x][y];
5082   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5083   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5084   int horiz_move = (dx != 0);
5085   int sign = (horiz_move ? dx : dy);
5086   int step = sign * element_info[element].move_stepsize;
5087
5088   // special values for move stepsize for spring and things on conveyor belt
5089   if (horiz_move)
5090   {
5091     if (CAN_FALL(element) &&
5092         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5093       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5094     else if (element == EL_SPRING)
5095       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5096   }
5097
5098   return step;
5099 }
5100
5101 static int getElementMoveStepsize(int x, int y)
5102 {
5103   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5104 }
5105
5106 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5107 {
5108   if (player->GfxAction != action || player->GfxDir != dir)
5109   {
5110     player->GfxAction = action;
5111     player->GfxDir = dir;
5112     player->Frame = 0;
5113     player->StepFrame = 0;
5114   }
5115 }
5116
5117 static void ResetGfxFrame(int x, int y)
5118 {
5119   // profiling showed that "autotest" spends 10~20% of its time in this function
5120   if (DrawingDeactivatedField())
5121     return;
5122
5123   int element = Tile[x][y];
5124   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5125
5126   if (graphic_info[graphic].anim_global_sync)
5127     GfxFrame[x][y] = FrameCounter;
5128   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5129     GfxFrame[x][y] = CustomValue[x][y];
5130   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5131     GfxFrame[x][y] = element_info[element].collect_score;
5132   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5133     GfxFrame[x][y] = ChangeDelay[x][y];
5134 }
5135
5136 static void ResetGfxAnimation(int x, int y)
5137 {
5138   GfxAction[x][y] = ACTION_DEFAULT;
5139   GfxDir[x][y] = MovDir[x][y];
5140   GfxFrame[x][y] = 0;
5141
5142   ResetGfxFrame(x, y);
5143 }
5144
5145 static void ResetRandomAnimationValue(int x, int y)
5146 {
5147   GfxRandom[x][y] = INIT_GFX_RANDOM();
5148 }
5149
5150 static void InitMovingField(int x, int y, int direction)
5151 {
5152   int element = Tile[x][y];
5153   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5154   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5155   int newx = x + dx;
5156   int newy = y + dy;
5157   boolean is_moving_before, is_moving_after;
5158
5159   // check if element was/is moving or being moved before/after mode change
5160   is_moving_before = (WasJustMoving[x][y] != 0);
5161   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5162
5163   // reset animation only for moving elements which change direction of moving
5164   // or which just started or stopped moving
5165   // (else CEs with property "can move" / "not moving" are reset each frame)
5166   if (is_moving_before != is_moving_after ||
5167       direction != MovDir[x][y])
5168     ResetGfxAnimation(x, y);
5169
5170   MovDir[x][y] = direction;
5171   GfxDir[x][y] = direction;
5172
5173   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5174                      direction == MV_DOWN && CAN_FALL(element) ?
5175                      ACTION_FALLING : ACTION_MOVING);
5176
5177   // this is needed for CEs with property "can move" / "not moving"
5178
5179   if (is_moving_after)
5180   {
5181     if (Tile[newx][newy] == EL_EMPTY)
5182       Tile[newx][newy] = EL_BLOCKED;
5183
5184     MovDir[newx][newy] = MovDir[x][y];
5185
5186     CustomValue[newx][newy] = CustomValue[x][y];
5187
5188     GfxFrame[newx][newy] = GfxFrame[x][y];
5189     GfxRandom[newx][newy] = GfxRandom[x][y];
5190     GfxAction[newx][newy] = GfxAction[x][y];
5191     GfxDir[newx][newy] = GfxDir[x][y];
5192   }
5193 }
5194
5195 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5196 {
5197   int direction = MovDir[x][y];
5198   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5199   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5200
5201   *goes_to_x = newx;
5202   *goes_to_y = newy;
5203 }
5204
5205 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5206 {
5207   int oldx = x, oldy = y;
5208   int direction = MovDir[x][y];
5209
5210   if (direction == MV_LEFT)
5211     oldx++;
5212   else if (direction == MV_RIGHT)
5213     oldx--;
5214   else if (direction == MV_UP)
5215     oldy++;
5216   else if (direction == MV_DOWN)
5217     oldy--;
5218
5219   *comes_from_x = oldx;
5220   *comes_from_y = oldy;
5221 }
5222
5223 static int MovingOrBlocked2Element(int x, int y)
5224 {
5225   int element = Tile[x][y];
5226
5227   if (element == EL_BLOCKED)
5228   {
5229     int oldx, oldy;
5230
5231     Blocked2Moving(x, y, &oldx, &oldy);
5232     return Tile[oldx][oldy];
5233   }
5234   else
5235     return element;
5236 }
5237
5238 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5239 {
5240   // like MovingOrBlocked2Element(), but if element is moving
5241   // and (x,y) is the field the moving element is just leaving,
5242   // return EL_BLOCKED instead of the element value
5243   int element = Tile[x][y];
5244
5245   if (IS_MOVING(x, y))
5246   {
5247     if (element == EL_BLOCKED)
5248     {
5249       int oldx, oldy;
5250
5251       Blocked2Moving(x, y, &oldx, &oldy);
5252       return Tile[oldx][oldy];
5253     }
5254     else
5255       return EL_BLOCKED;
5256   }
5257   else
5258     return element;
5259 }
5260
5261 static void RemoveField(int x, int y)
5262 {
5263   Tile[x][y] = EL_EMPTY;
5264
5265   MovPos[x][y] = 0;
5266   MovDir[x][y] = 0;
5267   MovDelay[x][y] = 0;
5268
5269   CustomValue[x][y] = 0;
5270
5271   AmoebaNr[x][y] = 0;
5272   ChangeDelay[x][y] = 0;
5273   ChangePage[x][y] = -1;
5274   Pushed[x][y] = FALSE;
5275
5276   GfxElement[x][y] = EL_UNDEFINED;
5277   GfxAction[x][y] = ACTION_DEFAULT;
5278   GfxDir[x][y] = MV_NONE;
5279 }
5280
5281 static void RemoveMovingField(int x, int y)
5282 {
5283   int oldx = x, oldy = y, newx = x, newy = y;
5284   int element = Tile[x][y];
5285   int next_element = EL_UNDEFINED;
5286
5287   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5288     return;
5289
5290   if (IS_MOVING(x, y))
5291   {
5292     Moving2Blocked(x, y, &newx, &newy);
5293
5294     if (Tile[newx][newy] != EL_BLOCKED)
5295     {
5296       // element is moving, but target field is not free (blocked), but
5297       // already occupied by something different (example: acid pool);
5298       // in this case, only remove the moving field, but not the target
5299
5300       RemoveField(oldx, oldy);
5301
5302       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5303
5304       TEST_DrawLevelField(oldx, oldy);
5305
5306       return;
5307     }
5308   }
5309   else if (element == EL_BLOCKED)
5310   {
5311     Blocked2Moving(x, y, &oldx, &oldy);
5312     if (!IS_MOVING(oldx, oldy))
5313       return;
5314   }
5315
5316   if (element == EL_BLOCKED &&
5317       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5318        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5319        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5320        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5321        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5322        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5323     next_element = get_next_element(Tile[oldx][oldy]);
5324
5325   RemoveField(oldx, oldy);
5326   RemoveField(newx, newy);
5327
5328   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5329
5330   if (next_element != EL_UNDEFINED)
5331     Tile[oldx][oldy] = next_element;
5332
5333   TEST_DrawLevelField(oldx, oldy);
5334   TEST_DrawLevelField(newx, newy);
5335 }
5336
5337 void DrawDynamite(int x, int y)
5338 {
5339   int sx = SCREENX(x), sy = SCREENY(y);
5340   int graphic = el2img(Tile[x][y]);
5341   int frame;
5342
5343   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5344     return;
5345
5346   if (IS_WALKABLE_INSIDE(Back[x][y]))
5347     return;
5348
5349   if (Back[x][y])
5350     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5351   else if (Store[x][y])
5352     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5353
5354   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5355
5356   if (Back[x][y] || Store[x][y])
5357     DrawGraphicThruMask(sx, sy, graphic, frame);
5358   else
5359     DrawGraphic(sx, sy, graphic, frame);
5360 }
5361
5362 static void CheckDynamite(int x, int y)
5363 {
5364   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5365   {
5366     MovDelay[x][y]--;
5367
5368     if (MovDelay[x][y] != 0)
5369     {
5370       DrawDynamite(x, y);
5371       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5372
5373       return;
5374     }
5375   }
5376
5377   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5378
5379   Bang(x, y);
5380 }
5381
5382 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5383 {
5384   boolean num_checked_players = 0;
5385   int i;
5386
5387   for (i = 0; i < MAX_PLAYERS; i++)
5388   {
5389     if (stored_player[i].active)
5390     {
5391       int sx = stored_player[i].jx;
5392       int sy = stored_player[i].jy;
5393
5394       if (num_checked_players == 0)
5395       {
5396         *sx1 = *sx2 = sx;
5397         *sy1 = *sy2 = sy;
5398       }
5399       else
5400       {
5401         *sx1 = MIN(*sx1, sx);
5402         *sy1 = MIN(*sy1, sy);
5403         *sx2 = MAX(*sx2, sx);
5404         *sy2 = MAX(*sy2, sy);
5405       }
5406
5407       num_checked_players++;
5408     }
5409   }
5410 }
5411
5412 static boolean checkIfAllPlayersFitToScreen_RND(void)
5413 {
5414   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5415
5416   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5417
5418   return (sx2 - sx1 < SCR_FIELDX &&
5419           sy2 - sy1 < SCR_FIELDY);
5420 }
5421
5422 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5423 {
5424   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5425
5426   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5427
5428   *sx = (sx1 + sx2) / 2;
5429   *sy = (sy1 + sy2) / 2;
5430 }
5431
5432 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5433                                boolean center_screen, boolean quick_relocation)
5434 {
5435   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5436   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5437   boolean no_delay = (tape.warp_forward);
5438   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5439   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5440   int new_scroll_x, new_scroll_y;
5441
5442   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5443   {
5444     // case 1: quick relocation inside visible screen (without scrolling)
5445
5446     RedrawPlayfield();
5447
5448     return;
5449   }
5450
5451   if (!level.shifted_relocation || center_screen)
5452   {
5453     // relocation _with_ centering of screen
5454
5455     new_scroll_x = SCROLL_POSITION_X(x);
5456     new_scroll_y = SCROLL_POSITION_Y(y);
5457   }
5458   else
5459   {
5460     // relocation _without_ centering of screen
5461
5462     int center_scroll_x = SCROLL_POSITION_X(old_x);
5463     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5464     int offset_x = x + (scroll_x - center_scroll_x);
5465     int offset_y = y + (scroll_y - center_scroll_y);
5466
5467     // for new screen position, apply previous offset to center position
5468     new_scroll_x = SCROLL_POSITION_X(offset_x);
5469     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5470   }
5471
5472   if (quick_relocation)
5473   {
5474     // case 2: quick relocation (redraw without visible scrolling)
5475
5476     scroll_x = new_scroll_x;
5477     scroll_y = new_scroll_y;
5478
5479     RedrawPlayfield();
5480
5481     return;
5482   }
5483
5484   // case 3: visible relocation (with scrolling to new position)
5485
5486   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5487
5488   SetVideoFrameDelay(wait_delay_value);
5489
5490   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5491   {
5492     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5493     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5494
5495     if (dx == 0 && dy == 0)             // no scrolling needed at all
5496       break;
5497
5498     scroll_x -= dx;
5499     scroll_y -= dy;
5500
5501     // set values for horizontal/vertical screen scrolling (half tile size)
5502     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5503     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5504     int pos_x = dx * TILEX / 2;
5505     int pos_y = dy * TILEY / 2;
5506     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5507     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5508
5509     ScrollLevel(dx, dy);
5510     DrawAllPlayers();
5511
5512     // scroll in two steps of half tile size to make things smoother
5513     BlitScreenToBitmapExt_RND(window, fx, fy);
5514
5515     // scroll second step to align at full tile size
5516     BlitScreenToBitmap(window);
5517   }
5518
5519   DrawAllPlayers();
5520   BackToFront();
5521
5522   SetVideoFrameDelay(frame_delay_value_old);
5523 }
5524
5525 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5526 {
5527   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5528   int player_nr = GET_PLAYER_NR(el_player);
5529   struct PlayerInfo *player = &stored_player[player_nr];
5530   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5531   boolean no_delay = (tape.warp_forward);
5532   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5533   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5534   int old_jx = player->jx;
5535   int old_jy = player->jy;
5536   int old_element = Tile[old_jx][old_jy];
5537   int element = Tile[jx][jy];
5538   boolean player_relocated = (old_jx != jx || old_jy != jy);
5539
5540   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5541   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5542   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5543   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5544   int leave_side_horiz = move_dir_horiz;
5545   int leave_side_vert  = move_dir_vert;
5546   int enter_side = enter_side_horiz | enter_side_vert;
5547   int leave_side = leave_side_horiz | leave_side_vert;
5548
5549   if (player->buried)           // do not reanimate dead player
5550     return;
5551
5552   if (!player_relocated)        // no need to relocate the player
5553     return;
5554
5555   if (IS_PLAYER(jx, jy))        // player already placed at new position
5556   {
5557     RemoveField(jx, jy);        // temporarily remove newly placed player
5558     DrawLevelField(jx, jy);
5559   }
5560
5561   if (player->present)
5562   {
5563     while (player->MovPos)
5564     {
5565       ScrollPlayer(player, SCROLL_GO_ON);
5566       ScrollScreen(NULL, SCROLL_GO_ON);
5567
5568       AdvanceFrameAndPlayerCounters(player->index_nr);
5569
5570       DrawPlayer(player);
5571
5572       BackToFront_WithFrameDelay(wait_delay_value);
5573     }
5574
5575     DrawPlayer(player);         // needed here only to cleanup last field
5576     DrawLevelField(player->jx, player->jy);     // remove player graphic
5577
5578     player->is_moving = FALSE;
5579   }
5580
5581   if (IS_CUSTOM_ELEMENT(old_element))
5582     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5583                                CE_LEFT_BY_PLAYER,
5584                                player->index_bit, leave_side);
5585
5586   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5587                                       CE_PLAYER_LEAVES_X,
5588                                       player->index_bit, leave_side);
5589
5590   Tile[jx][jy] = el_player;
5591   InitPlayerField(jx, jy, el_player, TRUE);
5592
5593   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5594      possible that the relocation target field did not contain a player element,
5595      but a walkable element, to which the new player was relocated -- in this
5596      case, restore that (already initialized!) element on the player field */
5597   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5598   {
5599     Tile[jx][jy] = element;     // restore previously existing element
5600   }
5601
5602   // only visually relocate centered player
5603   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5604                      FALSE, level.instant_relocation);
5605
5606   TestIfPlayerTouchesBadThing(jx, jy);
5607   TestIfPlayerTouchesCustomElement(jx, jy);
5608
5609   if (IS_CUSTOM_ELEMENT(element))
5610     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5611                                player->index_bit, enter_side);
5612
5613   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5614                                       player->index_bit, enter_side);
5615
5616   if (player->is_switching)
5617   {
5618     /* ensure that relocation while still switching an element does not cause
5619        a new element to be treated as also switched directly after relocation
5620        (this is important for teleporter switches that teleport the player to
5621        a place where another teleporter switch is in the same direction, which
5622        would then incorrectly be treated as immediately switched before the
5623        direction key that caused the switch was released) */
5624
5625     player->switch_x += jx - old_jx;
5626     player->switch_y += jy - old_jy;
5627   }
5628 }
5629
5630 static void Explode(int ex, int ey, int phase, int mode)
5631 {
5632   int x, y;
5633   int last_phase;
5634   int border_element;
5635
5636   // !!! eliminate this variable !!!
5637   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5638
5639   if (game.explosions_delayed)
5640   {
5641     ExplodeField[ex][ey] = mode;
5642     return;
5643   }
5644
5645   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5646   {
5647     int center_element = Tile[ex][ey];
5648     int artwork_element, explosion_element;     // set these values later
5649
5650     // remove things displayed in background while burning dynamite
5651     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5652       Back[ex][ey] = 0;
5653
5654     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5655     {
5656       // put moving element to center field (and let it explode there)
5657       center_element = MovingOrBlocked2Element(ex, ey);
5658       RemoveMovingField(ex, ey);
5659       Tile[ex][ey] = center_element;
5660     }
5661
5662     // now "center_element" is finally determined -- set related values now
5663     artwork_element = center_element;           // for custom player artwork
5664     explosion_element = center_element;         // for custom player artwork
5665
5666     if (IS_PLAYER(ex, ey))
5667     {
5668       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5669
5670       artwork_element = stored_player[player_nr].artwork_element;
5671
5672       if (level.use_explosion_element[player_nr])
5673       {
5674         explosion_element = level.explosion_element[player_nr];
5675         artwork_element = explosion_element;
5676       }
5677     }
5678
5679     if (mode == EX_TYPE_NORMAL ||
5680         mode == EX_TYPE_CENTER ||
5681         mode == EX_TYPE_CROSS)
5682       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5683
5684     last_phase = element_info[explosion_element].explosion_delay + 1;
5685
5686     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5687     {
5688       int xx = x - ex + 1;
5689       int yy = y - ey + 1;
5690       int element;
5691
5692       if (!IN_LEV_FIELD(x, y) ||
5693           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5694           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5695         continue;
5696
5697       element = Tile[x][y];
5698
5699       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5700       {
5701         element = MovingOrBlocked2Element(x, y);
5702
5703         if (!IS_EXPLOSION_PROOF(element))
5704           RemoveMovingField(x, y);
5705       }
5706
5707       // indestructible elements can only explode in center (but not flames)
5708       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5709                                            mode == EX_TYPE_BORDER)) ||
5710           element == EL_FLAMES)
5711         continue;
5712
5713       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5714          behaviour, for example when touching a yamyam that explodes to rocks
5715          with active deadly shield, a rock is created under the player !!! */
5716       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5717 #if 0
5718       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5719           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5720            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5721 #else
5722       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5723 #endif
5724       {
5725         if (IS_ACTIVE_BOMB(element))
5726         {
5727           // re-activate things under the bomb like gate or penguin
5728           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5729           Back[x][y] = 0;
5730         }
5731
5732         continue;
5733       }
5734
5735       // save walkable background elements while explosion on same tile
5736       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5737           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5738         Back[x][y] = element;
5739
5740       // ignite explodable elements reached by other explosion
5741       if (element == EL_EXPLOSION)
5742         element = Store2[x][y];
5743
5744       if (AmoebaNr[x][y] &&
5745           (element == EL_AMOEBA_FULL ||
5746            element == EL_BD_AMOEBA ||
5747            element == EL_AMOEBA_GROWING))
5748       {
5749         AmoebaCnt[AmoebaNr[x][y]]--;
5750         AmoebaCnt2[AmoebaNr[x][y]]--;
5751       }
5752
5753       RemoveField(x, y);
5754
5755       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5756       {
5757         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5758
5759         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5760
5761         if (PLAYERINFO(ex, ey)->use_murphy)
5762           Store[x][y] = EL_EMPTY;
5763       }
5764
5765       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5766       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5767       else if (ELEM_IS_PLAYER(center_element))
5768         Store[x][y] = EL_EMPTY;
5769       else if (center_element == EL_YAMYAM)
5770         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5771       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5772         Store[x][y] = element_info[center_element].content.e[xx][yy];
5773 #if 1
5774       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5775       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5776       // otherwise) -- FIX THIS !!!
5777       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5778         Store[x][y] = element_info[element].content.e[1][1];
5779 #else
5780       else if (!CAN_EXPLODE(element))
5781         Store[x][y] = element_info[element].content.e[1][1];
5782 #endif
5783       else
5784         Store[x][y] = EL_EMPTY;
5785
5786       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5787           center_element == EL_AMOEBA_TO_DIAMOND)
5788         Store2[x][y] = element;
5789
5790       Tile[x][y] = EL_EXPLOSION;
5791       GfxElement[x][y] = artwork_element;
5792
5793       ExplodePhase[x][y] = 1;
5794       ExplodeDelay[x][y] = last_phase;
5795
5796       Stop[x][y] = TRUE;
5797     }
5798
5799     if (center_element == EL_YAMYAM)
5800       game.yamyam_content_nr =
5801         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5802
5803     return;
5804   }
5805
5806   if (Stop[ex][ey])
5807     return;
5808
5809   x = ex;
5810   y = ey;
5811
5812   if (phase == 1)
5813     GfxFrame[x][y] = 0;         // restart explosion animation
5814
5815   last_phase = ExplodeDelay[x][y];
5816
5817   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5818
5819   // this can happen if the player leaves an explosion just in time
5820   if (GfxElement[x][y] == EL_UNDEFINED)
5821     GfxElement[x][y] = EL_EMPTY;
5822
5823   border_element = Store2[x][y];
5824   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5825     border_element = StorePlayer[x][y];
5826
5827   if (phase == element_info[border_element].ignition_delay ||
5828       phase == last_phase)
5829   {
5830     boolean border_explosion = FALSE;
5831
5832     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5833         !PLAYER_EXPLOSION_PROTECTED(x, y))
5834     {
5835       KillPlayerUnlessExplosionProtected(x, y);
5836       border_explosion = TRUE;
5837     }
5838     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5839     {
5840       Tile[x][y] = Store2[x][y];
5841       Store2[x][y] = 0;
5842       Bang(x, y);
5843       border_explosion = TRUE;
5844     }
5845     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5846     {
5847       AmoebaToDiamond(x, y);
5848       Store2[x][y] = 0;
5849       border_explosion = TRUE;
5850     }
5851
5852     // if an element just explodes due to another explosion (chain-reaction),
5853     // do not immediately end the new explosion when it was the last frame of
5854     // the explosion (as it would be done in the following "if"-statement!)
5855     if (border_explosion && phase == last_phase)
5856       return;
5857   }
5858
5859   if (phase == last_phase)
5860   {
5861     int element;
5862
5863     element = Tile[x][y] = Store[x][y];
5864     Store[x][y] = Store2[x][y] = 0;
5865     GfxElement[x][y] = EL_UNDEFINED;
5866
5867     // player can escape from explosions and might therefore be still alive
5868     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5869         element <= EL_PLAYER_IS_EXPLODING_4)
5870     {
5871       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5872       int explosion_element = EL_PLAYER_1 + player_nr;
5873       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5874       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5875
5876       if (level.use_explosion_element[player_nr])
5877         explosion_element = level.explosion_element[player_nr];
5878
5879       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5880                     element_info[explosion_element].content.e[xx][yy]);
5881     }
5882
5883     // restore probably existing indestructible background element
5884     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5885       element = Tile[x][y] = Back[x][y];
5886     Back[x][y] = 0;
5887
5888     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5889     GfxDir[x][y] = MV_NONE;
5890     ChangeDelay[x][y] = 0;
5891     ChangePage[x][y] = -1;
5892
5893     CustomValue[x][y] = 0;
5894
5895     InitField_WithBug2(x, y, FALSE);
5896
5897     TEST_DrawLevelField(x, y);
5898
5899     TestIfElementTouchesCustomElement(x, y);
5900
5901     if (GFX_CRUMBLED(element))
5902       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5903
5904     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5905       StorePlayer[x][y] = 0;
5906
5907     if (ELEM_IS_PLAYER(element))
5908       RelocatePlayer(x, y, element);
5909   }
5910   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5911   {
5912     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5913     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5914
5915     if (phase == delay)
5916       TEST_DrawLevelFieldCrumbled(x, y);
5917
5918     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5919     {
5920       DrawLevelElement(x, y, Back[x][y]);
5921       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5922     }
5923     else if (IS_WALKABLE_UNDER(Back[x][y]))
5924     {
5925       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5926       DrawLevelElementThruMask(x, y, Back[x][y]);
5927     }
5928     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5929       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5930   }
5931 }
5932
5933 static void DynaExplode(int ex, int ey)
5934 {
5935   int i, j;
5936   int dynabomb_element = Tile[ex][ey];
5937   int dynabomb_size = 1;
5938   boolean dynabomb_xl = FALSE;
5939   struct PlayerInfo *player;
5940   static int xy[4][2] =
5941   {
5942     { 0, -1 },
5943     { -1, 0 },
5944     { +1, 0 },
5945     { 0, +1 }
5946   };
5947
5948   if (IS_ACTIVE_BOMB(dynabomb_element))
5949   {
5950     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5951     dynabomb_size = player->dynabomb_size;
5952     dynabomb_xl = player->dynabomb_xl;
5953     player->dynabombs_left++;
5954   }
5955
5956   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5957
5958   for (i = 0; i < NUM_DIRECTIONS; i++)
5959   {
5960     for (j = 1; j <= dynabomb_size; j++)
5961     {
5962       int x = ex + j * xy[i][0];
5963       int y = ey + j * xy[i][1];
5964       int element;
5965
5966       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
5967         break;
5968
5969       element = Tile[x][y];
5970
5971       // do not restart explosions of fields with active bombs
5972       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5973         continue;
5974
5975       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5976
5977       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5978           !IS_DIGGABLE(element) && !dynabomb_xl)
5979         break;
5980     }
5981   }
5982 }
5983
5984 void Bang(int x, int y)
5985 {
5986   int element = MovingOrBlocked2Element(x, y);
5987   int explosion_type = EX_TYPE_NORMAL;
5988
5989   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5990   {
5991     struct PlayerInfo *player = PLAYERINFO(x, y);
5992
5993     element = Tile[x][y] = player->initial_element;
5994
5995     if (level.use_explosion_element[player->index_nr])
5996     {
5997       int explosion_element = level.explosion_element[player->index_nr];
5998
5999       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6000         explosion_type = EX_TYPE_CROSS;
6001       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6002         explosion_type = EX_TYPE_CENTER;
6003     }
6004   }
6005
6006   switch (element)
6007   {
6008     case EL_BUG:
6009     case EL_SPACESHIP:
6010     case EL_BD_BUTTERFLY:
6011     case EL_BD_FIREFLY:
6012     case EL_YAMYAM:
6013     case EL_DARK_YAMYAM:
6014     case EL_ROBOT:
6015     case EL_PACMAN:
6016     case EL_MOLE:
6017       RaiseScoreElement(element);
6018       break;
6019
6020     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6021     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6022     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6023     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6024     case EL_DYNABOMB_INCREASE_NUMBER:
6025     case EL_DYNABOMB_INCREASE_SIZE:
6026     case EL_DYNABOMB_INCREASE_POWER:
6027       explosion_type = EX_TYPE_DYNA;
6028       break;
6029
6030     case EL_DC_LANDMINE:
6031       explosion_type = EX_TYPE_CENTER;
6032       break;
6033
6034     case EL_PENGUIN:
6035     case EL_LAMP:
6036     case EL_LAMP_ACTIVE:
6037     case EL_AMOEBA_TO_DIAMOND:
6038       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6039         explosion_type = EX_TYPE_CENTER;
6040       break;
6041
6042     default:
6043       if (element_info[element].explosion_type == EXPLODES_CROSS)
6044         explosion_type = EX_TYPE_CROSS;
6045       else if (element_info[element].explosion_type == EXPLODES_1X1)
6046         explosion_type = EX_TYPE_CENTER;
6047       break;
6048   }
6049
6050   if (explosion_type == EX_TYPE_DYNA)
6051     DynaExplode(x, y);
6052   else
6053     Explode(x, y, EX_PHASE_START, explosion_type);
6054
6055   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6056 }
6057
6058 static void SplashAcid(int x, int y)
6059 {
6060   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6061       (!IN_LEV_FIELD(x - 1, y - 2) ||
6062        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6063     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6064
6065   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6066       (!IN_LEV_FIELD(x + 1, y - 2) ||
6067        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6068     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6069
6070   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6071 }
6072
6073 static void InitBeltMovement(void)
6074 {
6075   static int belt_base_element[4] =
6076   {
6077     EL_CONVEYOR_BELT_1_LEFT,
6078     EL_CONVEYOR_BELT_2_LEFT,
6079     EL_CONVEYOR_BELT_3_LEFT,
6080     EL_CONVEYOR_BELT_4_LEFT
6081   };
6082   static int belt_base_active_element[4] =
6083   {
6084     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6085     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6086     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6087     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6088   };
6089
6090   int x, y, i, j;
6091
6092   // set frame order for belt animation graphic according to belt direction
6093   for (i = 0; i < NUM_BELTS; i++)
6094   {
6095     int belt_nr = i;
6096
6097     for (j = 0; j < NUM_BELT_PARTS; j++)
6098     {
6099       int element = belt_base_active_element[belt_nr] + j;
6100       int graphic_1 = el2img(element);
6101       int graphic_2 = el2panelimg(element);
6102
6103       if (game.belt_dir[i] == MV_LEFT)
6104       {
6105         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6106         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6107       }
6108       else
6109       {
6110         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6111         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6112       }
6113     }
6114   }
6115
6116   SCAN_PLAYFIELD(x, y)
6117   {
6118     int element = Tile[x][y];
6119
6120     for (i = 0; i < NUM_BELTS; i++)
6121     {
6122       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6123       {
6124         int e_belt_nr = getBeltNrFromBeltElement(element);
6125         int belt_nr = i;
6126
6127         if (e_belt_nr == belt_nr)
6128         {
6129           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6130
6131           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6132         }
6133       }
6134     }
6135   }
6136 }
6137
6138 static void ToggleBeltSwitch(int x, int y)
6139 {
6140   static int belt_base_element[4] =
6141   {
6142     EL_CONVEYOR_BELT_1_LEFT,
6143     EL_CONVEYOR_BELT_2_LEFT,
6144     EL_CONVEYOR_BELT_3_LEFT,
6145     EL_CONVEYOR_BELT_4_LEFT
6146   };
6147   static int belt_base_active_element[4] =
6148   {
6149     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6150     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6151     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6152     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6153   };
6154   static int belt_base_switch_element[4] =
6155   {
6156     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6157     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6158     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6159     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6160   };
6161   static int belt_move_dir[4] =
6162   {
6163     MV_LEFT,
6164     MV_NONE,
6165     MV_RIGHT,
6166     MV_NONE,
6167   };
6168
6169   int element = Tile[x][y];
6170   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6171   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6172   int belt_dir = belt_move_dir[belt_dir_nr];
6173   int xx, yy, i;
6174
6175   if (!IS_BELT_SWITCH(element))
6176     return;
6177
6178   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6179   game.belt_dir[belt_nr] = belt_dir;
6180
6181   if (belt_dir_nr == 3)
6182     belt_dir_nr = 1;
6183
6184   // set frame order for belt animation graphic according to belt direction
6185   for (i = 0; i < NUM_BELT_PARTS; i++)
6186   {
6187     int element = belt_base_active_element[belt_nr] + i;
6188     int graphic_1 = el2img(element);
6189     int graphic_2 = el2panelimg(element);
6190
6191     if (belt_dir == MV_LEFT)
6192     {
6193       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6194       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6195     }
6196     else
6197     {
6198       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6199       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6200     }
6201   }
6202
6203   SCAN_PLAYFIELD(xx, yy)
6204   {
6205     int element = Tile[xx][yy];
6206
6207     if (IS_BELT_SWITCH(element))
6208     {
6209       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6210
6211       if (e_belt_nr == belt_nr)
6212       {
6213         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6214         TEST_DrawLevelField(xx, yy);
6215       }
6216     }
6217     else if (IS_BELT(element) && belt_dir != MV_NONE)
6218     {
6219       int e_belt_nr = getBeltNrFromBeltElement(element);
6220
6221       if (e_belt_nr == belt_nr)
6222       {
6223         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6224
6225         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6226         TEST_DrawLevelField(xx, yy);
6227       }
6228     }
6229     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6230     {
6231       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6232
6233       if (e_belt_nr == belt_nr)
6234       {
6235         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6236
6237         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6238         TEST_DrawLevelField(xx, yy);
6239       }
6240     }
6241   }
6242 }
6243
6244 static void ToggleSwitchgateSwitch(int x, int y)
6245 {
6246   int xx, yy;
6247
6248   game.switchgate_pos = !game.switchgate_pos;
6249
6250   SCAN_PLAYFIELD(xx, yy)
6251   {
6252     int element = Tile[xx][yy];
6253
6254     if (element == EL_SWITCHGATE_SWITCH_UP)
6255     {
6256       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6257       TEST_DrawLevelField(xx, yy);
6258     }
6259     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6260     {
6261       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6262       TEST_DrawLevelField(xx, yy);
6263     }
6264     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6265     {
6266       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6267       TEST_DrawLevelField(xx, yy);
6268     }
6269     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6270     {
6271       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6272       TEST_DrawLevelField(xx, yy);
6273     }
6274     else if (element == EL_SWITCHGATE_OPEN ||
6275              element == EL_SWITCHGATE_OPENING)
6276     {
6277       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6278
6279       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6280     }
6281     else if (element == EL_SWITCHGATE_CLOSED ||
6282              element == EL_SWITCHGATE_CLOSING)
6283     {
6284       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6285
6286       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6287     }
6288   }
6289 }
6290
6291 static int getInvisibleActiveFromInvisibleElement(int element)
6292 {
6293   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6294           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6295           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6296           element);
6297 }
6298
6299 static int getInvisibleFromInvisibleActiveElement(int element)
6300 {
6301   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6302           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6303           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6304           element);
6305 }
6306
6307 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6308 {
6309   int x, y;
6310
6311   SCAN_PLAYFIELD(x, y)
6312   {
6313     int element = Tile[x][y];
6314
6315     if (element == EL_LIGHT_SWITCH &&
6316         game.light_time_left > 0)
6317     {
6318       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6319       TEST_DrawLevelField(x, y);
6320     }
6321     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6322              game.light_time_left == 0)
6323     {
6324       Tile[x][y] = EL_LIGHT_SWITCH;
6325       TEST_DrawLevelField(x, y);
6326     }
6327     else if (element == EL_EMC_DRIPPER &&
6328              game.light_time_left > 0)
6329     {
6330       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6331       TEST_DrawLevelField(x, y);
6332     }
6333     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6334              game.light_time_left == 0)
6335     {
6336       Tile[x][y] = EL_EMC_DRIPPER;
6337       TEST_DrawLevelField(x, y);
6338     }
6339     else if (element == EL_INVISIBLE_STEELWALL ||
6340              element == EL_INVISIBLE_WALL ||
6341              element == EL_INVISIBLE_SAND)
6342     {
6343       if (game.light_time_left > 0)
6344         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6345
6346       TEST_DrawLevelField(x, y);
6347
6348       // uncrumble neighbour fields, if needed
6349       if (element == EL_INVISIBLE_SAND)
6350         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6351     }
6352     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6353              element == EL_INVISIBLE_WALL_ACTIVE ||
6354              element == EL_INVISIBLE_SAND_ACTIVE)
6355     {
6356       if (game.light_time_left == 0)
6357         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6358
6359       TEST_DrawLevelField(x, y);
6360
6361       // re-crumble neighbour fields, if needed
6362       if (element == EL_INVISIBLE_SAND)
6363         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6364     }
6365   }
6366 }
6367
6368 static void RedrawAllInvisibleElementsForLenses(void)
6369 {
6370   int x, y;
6371
6372   SCAN_PLAYFIELD(x, y)
6373   {
6374     int element = Tile[x][y];
6375
6376     if (element == EL_EMC_DRIPPER &&
6377         game.lenses_time_left > 0)
6378     {
6379       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6380       TEST_DrawLevelField(x, y);
6381     }
6382     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6383              game.lenses_time_left == 0)
6384     {
6385       Tile[x][y] = EL_EMC_DRIPPER;
6386       TEST_DrawLevelField(x, y);
6387     }
6388     else if (element == EL_INVISIBLE_STEELWALL ||
6389              element == EL_INVISIBLE_WALL ||
6390              element == EL_INVISIBLE_SAND)
6391     {
6392       if (game.lenses_time_left > 0)
6393         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6394
6395       TEST_DrawLevelField(x, y);
6396
6397       // uncrumble neighbour fields, if needed
6398       if (element == EL_INVISIBLE_SAND)
6399         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6400     }
6401     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6402              element == EL_INVISIBLE_WALL_ACTIVE ||
6403              element == EL_INVISIBLE_SAND_ACTIVE)
6404     {
6405       if (game.lenses_time_left == 0)
6406         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6407
6408       TEST_DrawLevelField(x, y);
6409
6410       // re-crumble neighbour fields, if needed
6411       if (element == EL_INVISIBLE_SAND)
6412         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6413     }
6414   }
6415 }
6416
6417 static void RedrawAllInvisibleElementsForMagnifier(void)
6418 {
6419   int x, y;
6420
6421   SCAN_PLAYFIELD(x, y)
6422   {
6423     int element = Tile[x][y];
6424
6425     if (element == EL_EMC_FAKE_GRASS &&
6426         game.magnify_time_left > 0)
6427     {
6428       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6429       TEST_DrawLevelField(x, y);
6430     }
6431     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6432              game.magnify_time_left == 0)
6433     {
6434       Tile[x][y] = EL_EMC_FAKE_GRASS;
6435       TEST_DrawLevelField(x, y);
6436     }
6437     else if (IS_GATE_GRAY(element) &&
6438              game.magnify_time_left > 0)
6439     {
6440       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6441                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6442                     IS_EM_GATE_GRAY(element) ?
6443                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6444                     IS_EMC_GATE_GRAY(element) ?
6445                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6446                     IS_DC_GATE_GRAY(element) ?
6447                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6448                     element);
6449       TEST_DrawLevelField(x, y);
6450     }
6451     else if (IS_GATE_GRAY_ACTIVE(element) &&
6452              game.magnify_time_left == 0)
6453     {
6454       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6455                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6456                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6457                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6458                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6459                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6460                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6461                     EL_DC_GATE_WHITE_GRAY :
6462                     element);
6463       TEST_DrawLevelField(x, y);
6464     }
6465   }
6466 }
6467
6468 static void ToggleLightSwitch(int x, int y)
6469 {
6470   int element = Tile[x][y];
6471
6472   game.light_time_left =
6473     (element == EL_LIGHT_SWITCH ?
6474      level.time_light * FRAMES_PER_SECOND : 0);
6475
6476   RedrawAllLightSwitchesAndInvisibleElements();
6477 }
6478
6479 static void ActivateTimegateSwitch(int x, int y)
6480 {
6481   int xx, yy;
6482
6483   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6484
6485   SCAN_PLAYFIELD(xx, yy)
6486   {
6487     int element = Tile[xx][yy];
6488
6489     if (element == EL_TIMEGATE_CLOSED ||
6490         element == EL_TIMEGATE_CLOSING)
6491     {
6492       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6493       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6494     }
6495
6496     /*
6497     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6498     {
6499       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6500       TEST_DrawLevelField(xx, yy);
6501     }
6502     */
6503
6504   }
6505
6506   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6507                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6508 }
6509
6510 static void Impact(int x, int y)
6511 {
6512   boolean last_line = (y == lev_fieldy - 1);
6513   boolean object_hit = FALSE;
6514   boolean impact = (last_line || object_hit);
6515   int element = Tile[x][y];
6516   int smashed = EL_STEELWALL;
6517
6518   if (!last_line)       // check if element below was hit
6519   {
6520     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6521       return;
6522
6523     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6524                                          MovDir[x][y + 1] != MV_DOWN ||
6525                                          MovPos[x][y + 1] <= TILEY / 2));
6526
6527     // do not smash moving elements that left the smashed field in time
6528     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6529         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6530       object_hit = FALSE;
6531
6532 #if USE_QUICKSAND_IMPACT_BUGFIX
6533     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6534     {
6535       RemoveMovingField(x, y + 1);
6536       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6537       Tile[x][y + 2] = EL_ROCK;
6538       TEST_DrawLevelField(x, y + 2);
6539
6540       object_hit = TRUE;
6541     }
6542
6543     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6544     {
6545       RemoveMovingField(x, y + 1);
6546       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6547       Tile[x][y + 2] = EL_ROCK;
6548       TEST_DrawLevelField(x, y + 2);
6549
6550       object_hit = TRUE;
6551     }
6552 #endif
6553
6554     if (object_hit)
6555       smashed = MovingOrBlocked2Element(x, y + 1);
6556
6557     impact = (last_line || object_hit);
6558   }
6559
6560   if (!last_line && smashed == EL_ACID) // element falls into acid
6561   {
6562     SplashAcid(x, y + 1);
6563     return;
6564   }
6565
6566   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6567   // only reset graphic animation if graphic really changes after impact
6568   if (impact &&
6569       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6570   {
6571     ResetGfxAnimation(x, y);
6572     TEST_DrawLevelField(x, y);
6573   }
6574
6575   if (impact && CAN_EXPLODE_IMPACT(element))
6576   {
6577     Bang(x, y);
6578     return;
6579   }
6580   else if (impact && element == EL_PEARL &&
6581            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6582   {
6583     ResetGfxAnimation(x, y);
6584
6585     Tile[x][y] = EL_PEARL_BREAKING;
6586     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6587     return;
6588   }
6589   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6590   {
6591     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6592
6593     return;
6594   }
6595
6596   if (impact && element == EL_AMOEBA_DROP)
6597   {
6598     if (object_hit && IS_PLAYER(x, y + 1))
6599       KillPlayerUnlessEnemyProtected(x, y + 1);
6600     else if (object_hit && smashed == EL_PENGUIN)
6601       Bang(x, y + 1);
6602     else
6603     {
6604       Tile[x][y] = EL_AMOEBA_GROWING;
6605       Store[x][y] = EL_AMOEBA_WET;
6606
6607       ResetRandomAnimationValue(x, y);
6608     }
6609     return;
6610   }
6611
6612   if (object_hit)               // check which object was hit
6613   {
6614     if ((CAN_PASS_MAGIC_WALL(element) && 
6615          (smashed == EL_MAGIC_WALL ||
6616           smashed == EL_BD_MAGIC_WALL)) ||
6617         (CAN_PASS_DC_MAGIC_WALL(element) &&
6618          smashed == EL_DC_MAGIC_WALL))
6619     {
6620       int xx, yy;
6621       int activated_magic_wall =
6622         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6623          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6624          EL_DC_MAGIC_WALL_ACTIVE);
6625
6626       // activate magic wall / mill
6627       SCAN_PLAYFIELD(xx, yy)
6628       {
6629         if (Tile[xx][yy] == smashed)
6630           Tile[xx][yy] = activated_magic_wall;
6631       }
6632
6633       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6634       game.magic_wall_active = TRUE;
6635
6636       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6637                             SND_MAGIC_WALL_ACTIVATING :
6638                             smashed == EL_BD_MAGIC_WALL ?
6639                             SND_BD_MAGIC_WALL_ACTIVATING :
6640                             SND_DC_MAGIC_WALL_ACTIVATING));
6641     }
6642
6643     if (IS_PLAYER(x, y + 1))
6644     {
6645       if (CAN_SMASH_PLAYER(element))
6646       {
6647         KillPlayerUnlessEnemyProtected(x, y + 1);
6648         return;
6649       }
6650     }
6651     else if (smashed == EL_PENGUIN)
6652     {
6653       if (CAN_SMASH_PLAYER(element))
6654       {
6655         Bang(x, y + 1);
6656         return;
6657       }
6658     }
6659     else if (element == EL_BD_DIAMOND)
6660     {
6661       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6662       {
6663         Bang(x, y + 1);
6664         return;
6665       }
6666     }
6667     else if (((element == EL_SP_INFOTRON ||
6668                element == EL_SP_ZONK) &&
6669               (smashed == EL_SP_SNIKSNAK ||
6670                smashed == EL_SP_ELECTRON ||
6671                smashed == EL_SP_DISK_ORANGE)) ||
6672              (element == EL_SP_INFOTRON &&
6673               smashed == EL_SP_DISK_YELLOW))
6674     {
6675       Bang(x, y + 1);
6676       return;
6677     }
6678     else if (CAN_SMASH_EVERYTHING(element))
6679     {
6680       if (IS_CLASSIC_ENEMY(smashed) ||
6681           CAN_EXPLODE_SMASHED(smashed))
6682       {
6683         Bang(x, y + 1);
6684         return;
6685       }
6686       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6687       {
6688         if (smashed == EL_LAMP ||
6689             smashed == EL_LAMP_ACTIVE)
6690         {
6691           Bang(x, y + 1);
6692           return;
6693         }
6694         else if (smashed == EL_NUT)
6695         {
6696           Tile[x][y + 1] = EL_NUT_BREAKING;
6697           PlayLevelSound(x, y, SND_NUT_BREAKING);
6698           RaiseScoreElement(EL_NUT);
6699           return;
6700         }
6701         else if (smashed == EL_PEARL)
6702         {
6703           ResetGfxAnimation(x, y);
6704
6705           Tile[x][y + 1] = EL_PEARL_BREAKING;
6706           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6707           return;
6708         }
6709         else if (smashed == EL_DIAMOND)
6710         {
6711           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6712           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6713           return;
6714         }
6715         else if (IS_BELT_SWITCH(smashed))
6716         {
6717           ToggleBeltSwitch(x, y + 1);
6718         }
6719         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6720                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6721                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6722                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6723         {
6724           ToggleSwitchgateSwitch(x, y + 1);
6725         }
6726         else if (smashed == EL_LIGHT_SWITCH ||
6727                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6728         {
6729           ToggleLightSwitch(x, y + 1);
6730         }
6731         else
6732         {
6733           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6734
6735           CheckElementChangeBySide(x, y + 1, smashed, element,
6736                                    CE_SWITCHED, CH_SIDE_TOP);
6737           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6738                                             CH_SIDE_TOP);
6739         }
6740       }
6741       else
6742       {
6743         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6744       }
6745     }
6746   }
6747
6748   // play sound of magic wall / mill
6749   if (!last_line &&
6750       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6751        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6752        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6753   {
6754     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6755       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6756     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6757       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6758     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6759       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6760
6761     return;
6762   }
6763
6764   // play sound of object that hits the ground
6765   if (last_line || object_hit)
6766     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6767 }
6768
6769 static void TurnRoundExt(int x, int y)
6770 {
6771   static struct
6772   {
6773     int dx, dy;
6774   } move_xy[] =
6775   {
6776     {  0,  0 },
6777     { -1,  0 },
6778     { +1,  0 },
6779     {  0,  0 },
6780     {  0, -1 },
6781     {  0,  0 }, { 0, 0 }, { 0, 0 },
6782     {  0, +1 }
6783   };
6784   static struct
6785   {
6786     int left, right, back;
6787   } turn[] =
6788   {
6789     { 0,        0,              0        },
6790     { MV_DOWN,  MV_UP,          MV_RIGHT },
6791     { MV_UP,    MV_DOWN,        MV_LEFT  },
6792     { 0,        0,              0        },
6793     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6794     { 0,        0,              0        },
6795     { 0,        0,              0        },
6796     { 0,        0,              0        },
6797     { MV_RIGHT, MV_LEFT,        MV_UP    }
6798   };
6799
6800   int element = Tile[x][y];
6801   int move_pattern = element_info[element].move_pattern;
6802
6803   int old_move_dir = MovDir[x][y];
6804   int left_dir  = turn[old_move_dir].left;
6805   int right_dir = turn[old_move_dir].right;
6806   int back_dir  = turn[old_move_dir].back;
6807
6808   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6809   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6810   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6811   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6812
6813   int left_x  = x + left_dx,  left_y  = y + left_dy;
6814   int right_x = x + right_dx, right_y = y + right_dy;
6815   int move_x  = x + move_dx,  move_y  = y + move_dy;
6816
6817   int xx, yy;
6818
6819   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6820   {
6821     TestIfBadThingTouchesOtherBadThing(x, y);
6822
6823     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6824       MovDir[x][y] = right_dir;
6825     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6826       MovDir[x][y] = left_dir;
6827
6828     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6829       MovDelay[x][y] = 9;
6830     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6831       MovDelay[x][y] = 1;
6832   }
6833   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6834   {
6835     TestIfBadThingTouchesOtherBadThing(x, y);
6836
6837     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6838       MovDir[x][y] = left_dir;
6839     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6840       MovDir[x][y] = right_dir;
6841
6842     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6843       MovDelay[x][y] = 9;
6844     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6845       MovDelay[x][y] = 1;
6846   }
6847   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6848   {
6849     TestIfBadThingTouchesOtherBadThing(x, y);
6850
6851     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6852       MovDir[x][y] = left_dir;
6853     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6854       MovDir[x][y] = right_dir;
6855
6856     if (MovDir[x][y] != old_move_dir)
6857       MovDelay[x][y] = 9;
6858   }
6859   else if (element == EL_YAMYAM)
6860   {
6861     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6862     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6863
6864     if (can_turn_left && can_turn_right)
6865       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6866     else if (can_turn_left)
6867       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6868     else if (can_turn_right)
6869       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6870     else
6871       MovDir[x][y] = back_dir;
6872
6873     MovDelay[x][y] = 16 + 16 * RND(3);
6874   }
6875   else if (element == EL_DARK_YAMYAM)
6876   {
6877     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6878                                                          left_x, left_y);
6879     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6880                                                          right_x, right_y);
6881
6882     if (can_turn_left && can_turn_right)
6883       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6884     else if (can_turn_left)
6885       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6886     else if (can_turn_right)
6887       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6888     else
6889       MovDir[x][y] = back_dir;
6890
6891     MovDelay[x][y] = 16 + 16 * RND(3);
6892   }
6893   else if (element == EL_PACMAN)
6894   {
6895     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6896     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6897
6898     if (can_turn_left && can_turn_right)
6899       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6900     else if (can_turn_left)
6901       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6902     else if (can_turn_right)
6903       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6904     else
6905       MovDir[x][y] = back_dir;
6906
6907     MovDelay[x][y] = 6 + RND(40);
6908   }
6909   else if (element == EL_PIG)
6910   {
6911     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6912     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6913     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6914     boolean should_turn_left, should_turn_right, should_move_on;
6915     int rnd_value = 24;
6916     int rnd = RND(rnd_value);
6917
6918     should_turn_left = (can_turn_left &&
6919                         (!can_move_on ||
6920                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6921                                                    y + back_dy + left_dy)));
6922     should_turn_right = (can_turn_right &&
6923                          (!can_move_on ||
6924                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6925                                                     y + back_dy + right_dy)));
6926     should_move_on = (can_move_on &&
6927                       (!can_turn_left ||
6928                        !can_turn_right ||
6929                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6930                                                  y + move_dy + left_dy) ||
6931                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6932                                                  y + move_dy + right_dy)));
6933
6934     if (should_turn_left || should_turn_right || should_move_on)
6935     {
6936       if (should_turn_left && should_turn_right && should_move_on)
6937         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6938                         rnd < 2 * rnd_value / 3 ? right_dir :
6939                         old_move_dir);
6940       else if (should_turn_left && should_turn_right)
6941         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6942       else if (should_turn_left && should_move_on)
6943         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6944       else if (should_turn_right && should_move_on)
6945         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6946       else if (should_turn_left)
6947         MovDir[x][y] = left_dir;
6948       else if (should_turn_right)
6949         MovDir[x][y] = right_dir;
6950       else if (should_move_on)
6951         MovDir[x][y] = old_move_dir;
6952     }
6953     else if (can_move_on && rnd > rnd_value / 8)
6954       MovDir[x][y] = old_move_dir;
6955     else if (can_turn_left && can_turn_right)
6956       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6957     else if (can_turn_left && rnd > rnd_value / 8)
6958       MovDir[x][y] = left_dir;
6959     else if (can_turn_right && rnd > rnd_value/8)
6960       MovDir[x][y] = right_dir;
6961     else
6962       MovDir[x][y] = back_dir;
6963
6964     xx = x + move_xy[MovDir[x][y]].dx;
6965     yy = y + move_xy[MovDir[x][y]].dy;
6966
6967     if (!IN_LEV_FIELD(xx, yy) ||
6968         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
6969       MovDir[x][y] = old_move_dir;
6970
6971     MovDelay[x][y] = 0;
6972   }
6973   else if (element == EL_DRAGON)
6974   {
6975     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6976     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6977     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6978     int rnd_value = 24;
6979     int rnd = RND(rnd_value);
6980
6981     if (can_move_on && rnd > rnd_value / 8)
6982       MovDir[x][y] = old_move_dir;
6983     else if (can_turn_left && can_turn_right)
6984       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6985     else if (can_turn_left && rnd > rnd_value / 8)
6986       MovDir[x][y] = left_dir;
6987     else if (can_turn_right && rnd > rnd_value / 8)
6988       MovDir[x][y] = right_dir;
6989     else
6990       MovDir[x][y] = back_dir;
6991
6992     xx = x + move_xy[MovDir[x][y]].dx;
6993     yy = y + move_xy[MovDir[x][y]].dy;
6994
6995     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6996       MovDir[x][y] = old_move_dir;
6997
6998     MovDelay[x][y] = 0;
6999   }
7000   else if (element == EL_MOLE)
7001   {
7002     boolean can_move_on =
7003       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7004                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7005                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7006     if (!can_move_on)
7007     {
7008       boolean can_turn_left =
7009         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7010                               IS_AMOEBOID(Tile[left_x][left_y])));
7011
7012       boolean can_turn_right =
7013         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7014                               IS_AMOEBOID(Tile[right_x][right_y])));
7015
7016       if (can_turn_left && can_turn_right)
7017         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7018       else if (can_turn_left)
7019         MovDir[x][y] = left_dir;
7020       else
7021         MovDir[x][y] = right_dir;
7022     }
7023
7024     if (MovDir[x][y] != old_move_dir)
7025       MovDelay[x][y] = 9;
7026   }
7027   else if (element == EL_BALLOON)
7028   {
7029     MovDir[x][y] = game.wind_direction;
7030     MovDelay[x][y] = 0;
7031   }
7032   else if (element == EL_SPRING)
7033   {
7034     if (MovDir[x][y] & MV_HORIZONTAL)
7035     {
7036       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7037           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7038       {
7039         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7040         ResetGfxAnimation(move_x, move_y);
7041         TEST_DrawLevelField(move_x, move_y);
7042
7043         MovDir[x][y] = back_dir;
7044       }
7045       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7046                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7047         MovDir[x][y] = MV_NONE;
7048     }
7049
7050     MovDelay[x][y] = 0;
7051   }
7052   else if (element == EL_ROBOT ||
7053            element == EL_SATELLITE ||
7054            element == EL_PENGUIN ||
7055            element == EL_EMC_ANDROID)
7056   {
7057     int attr_x = -1, attr_y = -1;
7058
7059     if (game.all_players_gone)
7060     {
7061       attr_x = game.exit_x;
7062       attr_y = game.exit_y;
7063     }
7064     else
7065     {
7066       int i;
7067
7068       for (i = 0; i < MAX_PLAYERS; i++)
7069       {
7070         struct PlayerInfo *player = &stored_player[i];
7071         int jx = player->jx, jy = player->jy;
7072
7073         if (!player->active)
7074           continue;
7075
7076         if (attr_x == -1 ||
7077             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7078         {
7079           attr_x = jx;
7080           attr_y = jy;
7081         }
7082       }
7083     }
7084
7085     if (element == EL_ROBOT &&
7086         game.robot_wheel_x >= 0 &&
7087         game.robot_wheel_y >= 0 &&
7088         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7089          game.engine_version < VERSION_IDENT(3,1,0,0)))
7090     {
7091       attr_x = game.robot_wheel_x;
7092       attr_y = game.robot_wheel_y;
7093     }
7094
7095     if (element == EL_PENGUIN)
7096     {
7097       int i;
7098       static int xy[4][2] =
7099       {
7100         { 0, -1 },
7101         { -1, 0 },
7102         { +1, 0 },
7103         { 0, +1 }
7104       };
7105
7106       for (i = 0; i < NUM_DIRECTIONS; i++)
7107       {
7108         int ex = x + xy[i][0];
7109         int ey = y + xy[i][1];
7110
7111         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7112                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7113                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7114                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7115         {
7116           attr_x = ex;
7117           attr_y = ey;
7118           break;
7119         }
7120       }
7121     }
7122
7123     MovDir[x][y] = MV_NONE;
7124     if (attr_x < x)
7125       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7126     else if (attr_x > x)
7127       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7128     if (attr_y < y)
7129       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7130     else if (attr_y > y)
7131       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7132
7133     if (element == EL_ROBOT)
7134     {
7135       int newx, newy;
7136
7137       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7138         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7139       Moving2Blocked(x, y, &newx, &newy);
7140
7141       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7142         MovDelay[x][y] = 8 + 8 * !RND(3);
7143       else
7144         MovDelay[x][y] = 16;
7145     }
7146     else if (element == EL_PENGUIN)
7147     {
7148       int newx, newy;
7149
7150       MovDelay[x][y] = 1;
7151
7152       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7153       {
7154         boolean first_horiz = RND(2);
7155         int new_move_dir = MovDir[x][y];
7156
7157         MovDir[x][y] =
7158           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7159         Moving2Blocked(x, y, &newx, &newy);
7160
7161         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7162           return;
7163
7164         MovDir[x][y] =
7165           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7166         Moving2Blocked(x, y, &newx, &newy);
7167
7168         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7169           return;
7170
7171         MovDir[x][y] = old_move_dir;
7172         return;
7173       }
7174     }
7175     else if (element == EL_SATELLITE)
7176     {
7177       int newx, newy;
7178
7179       MovDelay[x][y] = 1;
7180
7181       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7182       {
7183         boolean first_horiz = RND(2);
7184         int new_move_dir = MovDir[x][y];
7185
7186         MovDir[x][y] =
7187           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7188         Moving2Blocked(x, y, &newx, &newy);
7189
7190         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7191           return;
7192
7193         MovDir[x][y] =
7194           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7195         Moving2Blocked(x, y, &newx, &newy);
7196
7197         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7198           return;
7199
7200         MovDir[x][y] = old_move_dir;
7201         return;
7202       }
7203     }
7204     else if (element == EL_EMC_ANDROID)
7205     {
7206       static int check_pos[16] =
7207       {
7208         -1,             //  0 => (invalid)
7209         7,              //  1 => MV_LEFT
7210         3,              //  2 => MV_RIGHT
7211         -1,             //  3 => (invalid)
7212         1,              //  4 =>            MV_UP
7213         0,              //  5 => MV_LEFT  | MV_UP
7214         2,              //  6 => MV_RIGHT | MV_UP
7215         -1,             //  7 => (invalid)
7216         5,              //  8 =>            MV_DOWN
7217         6,              //  9 => MV_LEFT  | MV_DOWN
7218         4,              // 10 => MV_RIGHT | MV_DOWN
7219         -1,             // 11 => (invalid)
7220         -1,             // 12 => (invalid)
7221         -1,             // 13 => (invalid)
7222         -1,             // 14 => (invalid)
7223         -1,             // 15 => (invalid)
7224       };
7225       static struct
7226       {
7227         int dx, dy;
7228         int dir;
7229       } check_xy[8] =
7230       {
7231         { -1, -1,       MV_LEFT  | MV_UP   },
7232         {  0, -1,                  MV_UP   },
7233         { +1, -1,       MV_RIGHT | MV_UP   },
7234         { +1,  0,       MV_RIGHT           },
7235         { +1, +1,       MV_RIGHT | MV_DOWN },
7236         {  0, +1,                  MV_DOWN },
7237         { -1, +1,       MV_LEFT  | MV_DOWN },
7238         { -1,  0,       MV_LEFT            },
7239       };
7240       int start_pos, check_order;
7241       boolean can_clone = FALSE;
7242       int i;
7243
7244       // check if there is any free field around current position
7245       for (i = 0; i < 8; i++)
7246       {
7247         int newx = x + check_xy[i].dx;
7248         int newy = y + check_xy[i].dy;
7249
7250         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7251         {
7252           can_clone = TRUE;
7253
7254           break;
7255         }
7256       }
7257
7258       if (can_clone)            // randomly find an element to clone
7259       {
7260         can_clone = FALSE;
7261
7262         start_pos = check_pos[RND(8)];
7263         check_order = (RND(2) ? -1 : +1);
7264
7265         for (i = 0; i < 8; i++)
7266         {
7267           int pos_raw = start_pos + i * check_order;
7268           int pos = (pos_raw + 8) % 8;
7269           int newx = x + check_xy[pos].dx;
7270           int newy = y + check_xy[pos].dy;
7271
7272           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7273           {
7274             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7275             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7276
7277             Store[x][y] = Tile[newx][newy];
7278
7279             can_clone = TRUE;
7280
7281             break;
7282           }
7283         }
7284       }
7285
7286       if (can_clone)            // randomly find a direction to move
7287       {
7288         can_clone = FALSE;
7289
7290         start_pos = check_pos[RND(8)];
7291         check_order = (RND(2) ? -1 : +1);
7292
7293         for (i = 0; i < 8; i++)
7294         {
7295           int pos_raw = start_pos + i * check_order;
7296           int pos = (pos_raw + 8) % 8;
7297           int newx = x + check_xy[pos].dx;
7298           int newy = y + check_xy[pos].dy;
7299           int new_move_dir = check_xy[pos].dir;
7300
7301           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7302           {
7303             MovDir[x][y] = new_move_dir;
7304             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7305
7306             can_clone = TRUE;
7307
7308             break;
7309           }
7310         }
7311       }
7312
7313       if (can_clone)            // cloning and moving successful
7314         return;
7315
7316       // cannot clone -- try to move towards player
7317
7318       start_pos = check_pos[MovDir[x][y] & 0x0f];
7319       check_order = (RND(2) ? -1 : +1);
7320
7321       for (i = 0; i < 3; i++)
7322       {
7323         // first check start_pos, then previous/next or (next/previous) pos
7324         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7325         int pos = (pos_raw + 8) % 8;
7326         int newx = x + check_xy[pos].dx;
7327         int newy = y + check_xy[pos].dy;
7328         int new_move_dir = check_xy[pos].dir;
7329
7330         if (IS_PLAYER(newx, newy))
7331           break;
7332
7333         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7334         {
7335           MovDir[x][y] = new_move_dir;
7336           MovDelay[x][y] = level.android_move_time * 8 + 1;
7337
7338           break;
7339         }
7340       }
7341     }
7342   }
7343   else if (move_pattern == MV_TURNING_LEFT ||
7344            move_pattern == MV_TURNING_RIGHT ||
7345            move_pattern == MV_TURNING_LEFT_RIGHT ||
7346            move_pattern == MV_TURNING_RIGHT_LEFT ||
7347            move_pattern == MV_TURNING_RANDOM ||
7348            move_pattern == MV_ALL_DIRECTIONS)
7349   {
7350     boolean can_turn_left =
7351       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7352     boolean can_turn_right =
7353       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7354
7355     if (element_info[element].move_stepsize == 0)       // "not moving"
7356       return;
7357
7358     if (move_pattern == MV_TURNING_LEFT)
7359       MovDir[x][y] = left_dir;
7360     else if (move_pattern == MV_TURNING_RIGHT)
7361       MovDir[x][y] = right_dir;
7362     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7363       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7364     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7365       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7366     else if (move_pattern == MV_TURNING_RANDOM)
7367       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7368                       can_turn_right && !can_turn_left ? right_dir :
7369                       RND(2) ? left_dir : right_dir);
7370     else if (can_turn_left && can_turn_right)
7371       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7372     else if (can_turn_left)
7373       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7374     else if (can_turn_right)
7375       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7376     else
7377       MovDir[x][y] = back_dir;
7378
7379     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7380   }
7381   else if (move_pattern == MV_HORIZONTAL ||
7382            move_pattern == MV_VERTICAL)
7383   {
7384     if (move_pattern & old_move_dir)
7385       MovDir[x][y] = back_dir;
7386     else if (move_pattern == MV_HORIZONTAL)
7387       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7388     else if (move_pattern == MV_VERTICAL)
7389       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7390
7391     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7392   }
7393   else if (move_pattern & MV_ANY_DIRECTION)
7394   {
7395     MovDir[x][y] = move_pattern;
7396     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7397   }
7398   else if (move_pattern & MV_WIND_DIRECTION)
7399   {
7400     MovDir[x][y] = game.wind_direction;
7401     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7402   }
7403   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7404   {
7405     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7406       MovDir[x][y] = left_dir;
7407     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7408       MovDir[x][y] = right_dir;
7409
7410     if (MovDir[x][y] != old_move_dir)
7411       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7412   }
7413   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7414   {
7415     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7416       MovDir[x][y] = right_dir;
7417     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7418       MovDir[x][y] = left_dir;
7419
7420     if (MovDir[x][y] != old_move_dir)
7421       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7422   }
7423   else if (move_pattern == MV_TOWARDS_PLAYER ||
7424            move_pattern == MV_AWAY_FROM_PLAYER)
7425   {
7426     int attr_x = -1, attr_y = -1;
7427     int newx, newy;
7428     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7429
7430     if (game.all_players_gone)
7431     {
7432       attr_x = game.exit_x;
7433       attr_y = game.exit_y;
7434     }
7435     else
7436     {
7437       int i;
7438
7439       for (i = 0; i < MAX_PLAYERS; i++)
7440       {
7441         struct PlayerInfo *player = &stored_player[i];
7442         int jx = player->jx, jy = player->jy;
7443
7444         if (!player->active)
7445           continue;
7446
7447         if (attr_x == -1 ||
7448             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7449         {
7450           attr_x = jx;
7451           attr_y = jy;
7452         }
7453       }
7454     }
7455
7456     MovDir[x][y] = MV_NONE;
7457     if (attr_x < x)
7458       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7459     else if (attr_x > x)
7460       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7461     if (attr_y < y)
7462       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7463     else if (attr_y > y)
7464       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7465
7466     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7467
7468     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7469     {
7470       boolean first_horiz = RND(2);
7471       int new_move_dir = MovDir[x][y];
7472
7473       if (element_info[element].move_stepsize == 0)     // "not moving"
7474       {
7475         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7476         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7477
7478         return;
7479       }
7480
7481       MovDir[x][y] =
7482         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7483       Moving2Blocked(x, y, &newx, &newy);
7484
7485       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7486         return;
7487
7488       MovDir[x][y] =
7489         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7490       Moving2Blocked(x, y, &newx, &newy);
7491
7492       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7493         return;
7494
7495       MovDir[x][y] = old_move_dir;
7496     }
7497   }
7498   else if (move_pattern == MV_WHEN_PUSHED ||
7499            move_pattern == MV_WHEN_DROPPED)
7500   {
7501     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7502       MovDir[x][y] = MV_NONE;
7503
7504     MovDelay[x][y] = 0;
7505   }
7506   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7507   {
7508     static int test_xy[7][2] =
7509     {
7510       { 0, -1 },
7511       { -1, 0 },
7512       { +1, 0 },
7513       { 0, +1 },
7514       { 0, -1 },
7515       { -1, 0 },
7516       { +1, 0 },
7517     };
7518     static int test_dir[7] =
7519     {
7520       MV_UP,
7521       MV_LEFT,
7522       MV_RIGHT,
7523       MV_DOWN,
7524       MV_UP,
7525       MV_LEFT,
7526       MV_RIGHT,
7527     };
7528     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7529     int move_preference = -1000000;     // start with very low preference
7530     int new_move_dir = MV_NONE;
7531     int start_test = RND(4);
7532     int i;
7533
7534     for (i = 0; i < NUM_DIRECTIONS; i++)
7535     {
7536       int move_dir = test_dir[start_test + i];
7537       int move_dir_preference;
7538
7539       xx = x + test_xy[start_test + i][0];
7540       yy = y + test_xy[start_test + i][1];
7541
7542       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7543           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7544       {
7545         new_move_dir = move_dir;
7546
7547         break;
7548       }
7549
7550       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7551         continue;
7552
7553       move_dir_preference = -1 * RunnerVisit[xx][yy];
7554       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7555         move_dir_preference = PlayerVisit[xx][yy];
7556
7557       if (move_dir_preference > move_preference)
7558       {
7559         // prefer field that has not been visited for the longest time
7560         move_preference = move_dir_preference;
7561         new_move_dir = move_dir;
7562       }
7563       else if (move_dir_preference == move_preference &&
7564                move_dir == old_move_dir)
7565       {
7566         // prefer last direction when all directions are preferred equally
7567         move_preference = move_dir_preference;
7568         new_move_dir = move_dir;
7569       }
7570     }
7571
7572     MovDir[x][y] = new_move_dir;
7573     if (old_move_dir != new_move_dir)
7574       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7575   }
7576 }
7577
7578 static void TurnRound(int x, int y)
7579 {
7580   int direction = MovDir[x][y];
7581
7582   TurnRoundExt(x, y);
7583
7584   GfxDir[x][y] = MovDir[x][y];
7585
7586   if (direction != MovDir[x][y])
7587     GfxFrame[x][y] = 0;
7588
7589   if (MovDelay[x][y])
7590     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7591
7592   ResetGfxFrame(x, y);
7593 }
7594
7595 static boolean JustBeingPushed(int x, int y)
7596 {
7597   int i;
7598
7599   for (i = 0; i < MAX_PLAYERS; i++)
7600   {
7601     struct PlayerInfo *player = &stored_player[i];
7602
7603     if (player->active && player->is_pushing && player->MovPos)
7604     {
7605       int next_jx = player->jx + (player->jx - player->last_jx);
7606       int next_jy = player->jy + (player->jy - player->last_jy);
7607
7608       if (x == next_jx && y == next_jy)
7609         return TRUE;
7610     }
7611   }
7612
7613   return FALSE;
7614 }
7615
7616 static void StartMoving(int x, int y)
7617 {
7618   boolean started_moving = FALSE;       // some elements can fall _and_ move
7619   int element = Tile[x][y];
7620
7621   if (Stop[x][y])
7622     return;
7623
7624   if (MovDelay[x][y] == 0)
7625     GfxAction[x][y] = ACTION_DEFAULT;
7626
7627   if (CAN_FALL(element) && y < lev_fieldy - 1)
7628   {
7629     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7630         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7631       if (JustBeingPushed(x, y))
7632         return;
7633
7634     if (element == EL_QUICKSAND_FULL)
7635     {
7636       if (IS_FREE(x, y + 1))
7637       {
7638         InitMovingField(x, y, MV_DOWN);
7639         started_moving = TRUE;
7640
7641         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7642 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7643         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7644           Store[x][y] = EL_ROCK;
7645 #else
7646         Store[x][y] = EL_ROCK;
7647 #endif
7648
7649         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7650       }
7651       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7652       {
7653         if (!MovDelay[x][y])
7654         {
7655           MovDelay[x][y] = TILEY + 1;
7656
7657           ResetGfxAnimation(x, y);
7658           ResetGfxAnimation(x, y + 1);
7659         }
7660
7661         if (MovDelay[x][y])
7662         {
7663           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7664           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7665
7666           MovDelay[x][y]--;
7667           if (MovDelay[x][y])
7668             return;
7669         }
7670
7671         Tile[x][y] = EL_QUICKSAND_EMPTY;
7672         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7673         Store[x][y + 1] = Store[x][y];
7674         Store[x][y] = 0;
7675
7676         PlayLevelSoundAction(x, y, ACTION_FILLING);
7677       }
7678       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7679       {
7680         if (!MovDelay[x][y])
7681         {
7682           MovDelay[x][y] = TILEY + 1;
7683
7684           ResetGfxAnimation(x, y);
7685           ResetGfxAnimation(x, y + 1);
7686         }
7687
7688         if (MovDelay[x][y])
7689         {
7690           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7691           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7692
7693           MovDelay[x][y]--;
7694           if (MovDelay[x][y])
7695             return;
7696         }
7697
7698         Tile[x][y] = EL_QUICKSAND_EMPTY;
7699         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7700         Store[x][y + 1] = Store[x][y];
7701         Store[x][y] = 0;
7702
7703         PlayLevelSoundAction(x, y, ACTION_FILLING);
7704       }
7705     }
7706     else if (element == EL_QUICKSAND_FAST_FULL)
7707     {
7708       if (IS_FREE(x, y + 1))
7709       {
7710         InitMovingField(x, y, MV_DOWN);
7711         started_moving = TRUE;
7712
7713         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7714 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7715         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7716           Store[x][y] = EL_ROCK;
7717 #else
7718         Store[x][y] = EL_ROCK;
7719 #endif
7720
7721         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7722       }
7723       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7724       {
7725         if (!MovDelay[x][y])
7726         {
7727           MovDelay[x][y] = TILEY + 1;
7728
7729           ResetGfxAnimation(x, y);
7730           ResetGfxAnimation(x, y + 1);
7731         }
7732
7733         if (MovDelay[x][y])
7734         {
7735           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7736           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7737
7738           MovDelay[x][y]--;
7739           if (MovDelay[x][y])
7740             return;
7741         }
7742
7743         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7744         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7745         Store[x][y + 1] = Store[x][y];
7746         Store[x][y] = 0;
7747
7748         PlayLevelSoundAction(x, y, ACTION_FILLING);
7749       }
7750       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7751       {
7752         if (!MovDelay[x][y])
7753         {
7754           MovDelay[x][y] = TILEY + 1;
7755
7756           ResetGfxAnimation(x, y);
7757           ResetGfxAnimation(x, y + 1);
7758         }
7759
7760         if (MovDelay[x][y])
7761         {
7762           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7763           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7764
7765           MovDelay[x][y]--;
7766           if (MovDelay[x][y])
7767             return;
7768         }
7769
7770         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7771         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7772         Store[x][y + 1] = Store[x][y];
7773         Store[x][y] = 0;
7774
7775         PlayLevelSoundAction(x, y, ACTION_FILLING);
7776       }
7777     }
7778     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7779              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7780     {
7781       InitMovingField(x, y, MV_DOWN);
7782       started_moving = TRUE;
7783
7784       Tile[x][y] = EL_QUICKSAND_FILLING;
7785       Store[x][y] = element;
7786
7787       PlayLevelSoundAction(x, y, ACTION_FILLING);
7788     }
7789     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7790              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7791     {
7792       InitMovingField(x, y, MV_DOWN);
7793       started_moving = TRUE;
7794
7795       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7796       Store[x][y] = element;
7797
7798       PlayLevelSoundAction(x, y, ACTION_FILLING);
7799     }
7800     else if (element == EL_MAGIC_WALL_FULL)
7801     {
7802       if (IS_FREE(x, y + 1))
7803       {
7804         InitMovingField(x, y, MV_DOWN);
7805         started_moving = TRUE;
7806
7807         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7808         Store[x][y] = EL_CHANGED(Store[x][y]);
7809       }
7810       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7811       {
7812         if (!MovDelay[x][y])
7813           MovDelay[x][y] = TILEY / 4 + 1;
7814
7815         if (MovDelay[x][y])
7816         {
7817           MovDelay[x][y]--;
7818           if (MovDelay[x][y])
7819             return;
7820         }
7821
7822         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7823         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7824         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7825         Store[x][y] = 0;
7826       }
7827     }
7828     else if (element == EL_BD_MAGIC_WALL_FULL)
7829     {
7830       if (IS_FREE(x, y + 1))
7831       {
7832         InitMovingField(x, y, MV_DOWN);
7833         started_moving = TRUE;
7834
7835         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7836         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7837       }
7838       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7839       {
7840         if (!MovDelay[x][y])
7841           MovDelay[x][y] = TILEY / 4 + 1;
7842
7843         if (MovDelay[x][y])
7844         {
7845           MovDelay[x][y]--;
7846           if (MovDelay[x][y])
7847             return;
7848         }
7849
7850         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7851         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7852         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7853         Store[x][y] = 0;
7854       }
7855     }
7856     else if (element == EL_DC_MAGIC_WALL_FULL)
7857     {
7858       if (IS_FREE(x, y + 1))
7859       {
7860         InitMovingField(x, y, MV_DOWN);
7861         started_moving = TRUE;
7862
7863         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7864         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7865       }
7866       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7867       {
7868         if (!MovDelay[x][y])
7869           MovDelay[x][y] = TILEY / 4 + 1;
7870
7871         if (MovDelay[x][y])
7872         {
7873           MovDelay[x][y]--;
7874           if (MovDelay[x][y])
7875             return;
7876         }
7877
7878         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7879         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7880         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7881         Store[x][y] = 0;
7882       }
7883     }
7884     else if ((CAN_PASS_MAGIC_WALL(element) &&
7885               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7886                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7887              (CAN_PASS_DC_MAGIC_WALL(element) &&
7888               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7889
7890     {
7891       InitMovingField(x, y, MV_DOWN);
7892       started_moving = TRUE;
7893
7894       Tile[x][y] =
7895         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7896          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7897          EL_DC_MAGIC_WALL_FILLING);
7898       Store[x][y] = element;
7899     }
7900     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
7901     {
7902       SplashAcid(x, y + 1);
7903
7904       InitMovingField(x, y, MV_DOWN);
7905       started_moving = TRUE;
7906
7907       Store[x][y] = EL_ACID;
7908     }
7909     else if (
7910              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7911               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7912              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7913               CAN_FALL(element) && WasJustFalling[x][y] &&
7914               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7915
7916              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7917               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7918               (Tile[x][y + 1] == EL_BLOCKED)))
7919     {
7920       /* this is needed for a special case not covered by calling "Impact()"
7921          from "ContinueMoving()": if an element moves to a tile directly below
7922          another element which was just falling on that tile (which was empty
7923          in the previous frame), the falling element above would just stop
7924          instead of smashing the element below (in previous version, the above
7925          element was just checked for "moving" instead of "falling", resulting
7926          in incorrect smashes caused by horizontal movement of the above
7927          element; also, the case of the player being the element to smash was
7928          simply not covered here... :-/ ) */
7929
7930       CheckCollision[x][y] = 0;
7931       CheckImpact[x][y] = 0;
7932
7933       Impact(x, y);
7934     }
7935     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7936     {
7937       if (MovDir[x][y] == MV_NONE)
7938       {
7939         InitMovingField(x, y, MV_DOWN);
7940         started_moving = TRUE;
7941       }
7942     }
7943     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
7944     {
7945       if (WasJustFalling[x][y]) // prevent animation from being restarted
7946         MovDir[x][y] = MV_DOWN;
7947
7948       InitMovingField(x, y, MV_DOWN);
7949       started_moving = TRUE;
7950     }
7951     else if (element == EL_AMOEBA_DROP)
7952     {
7953       Tile[x][y] = EL_AMOEBA_GROWING;
7954       Store[x][y] = EL_AMOEBA_WET;
7955     }
7956     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7957               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
7958              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7959              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7960     {
7961       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7962                                 (IS_FREE(x - 1, y + 1) ||
7963                                  Tile[x - 1][y + 1] == EL_ACID));
7964       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7965                                 (IS_FREE(x + 1, y + 1) ||
7966                                  Tile[x + 1][y + 1] == EL_ACID));
7967       boolean can_fall_any  = (can_fall_left || can_fall_right);
7968       boolean can_fall_both = (can_fall_left && can_fall_right);
7969       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
7970
7971       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7972       {
7973         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7974           can_fall_right = FALSE;
7975         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7976           can_fall_left = FALSE;
7977         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7978           can_fall_right = FALSE;
7979         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7980           can_fall_left = FALSE;
7981
7982         can_fall_any  = (can_fall_left || can_fall_right);
7983         can_fall_both = FALSE;
7984       }
7985
7986       if (can_fall_both)
7987       {
7988         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7989           can_fall_right = FALSE;       // slip down on left side
7990         else
7991           can_fall_left = !(can_fall_right = RND(2));
7992
7993         can_fall_both = FALSE;
7994       }
7995
7996       if (can_fall_any)
7997       {
7998         // if not determined otherwise, prefer left side for slipping down
7999         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8000         started_moving = TRUE;
8001       }
8002     }
8003     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8004     {
8005       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8006       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8007       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8008       int belt_dir = game.belt_dir[belt_nr];
8009
8010       if ((belt_dir == MV_LEFT  && left_is_free) ||
8011           (belt_dir == MV_RIGHT && right_is_free))
8012       {
8013         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8014
8015         InitMovingField(x, y, belt_dir);
8016         started_moving = TRUE;
8017
8018         Pushed[x][y] = TRUE;
8019         Pushed[nextx][y] = TRUE;
8020
8021         GfxAction[x][y] = ACTION_DEFAULT;
8022       }
8023       else
8024       {
8025         MovDir[x][y] = 0;       // if element was moving, stop it
8026       }
8027     }
8028   }
8029
8030   // not "else if" because of elements that can fall and move (EL_SPRING)
8031   if (CAN_MOVE(element) && !started_moving)
8032   {
8033     int move_pattern = element_info[element].move_pattern;
8034     int newx, newy;
8035
8036     Moving2Blocked(x, y, &newx, &newy);
8037
8038     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8039       return;
8040
8041     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8042         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8043     {
8044       WasJustMoving[x][y] = 0;
8045       CheckCollision[x][y] = 0;
8046
8047       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8048
8049       if (Tile[x][y] != element)        // element has changed
8050         return;
8051     }
8052
8053     if (!MovDelay[x][y])        // start new movement phase
8054     {
8055       // all objects that can change their move direction after each step
8056       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8057
8058       if (element != EL_YAMYAM &&
8059           element != EL_DARK_YAMYAM &&
8060           element != EL_PACMAN &&
8061           !(move_pattern & MV_ANY_DIRECTION) &&
8062           move_pattern != MV_TURNING_LEFT &&
8063           move_pattern != MV_TURNING_RIGHT &&
8064           move_pattern != MV_TURNING_LEFT_RIGHT &&
8065           move_pattern != MV_TURNING_RIGHT_LEFT &&
8066           move_pattern != MV_TURNING_RANDOM)
8067       {
8068         TurnRound(x, y);
8069
8070         if (MovDelay[x][y] && (element == EL_BUG ||
8071                                element == EL_SPACESHIP ||
8072                                element == EL_SP_SNIKSNAK ||
8073                                element == EL_SP_ELECTRON ||
8074                                element == EL_MOLE))
8075           TEST_DrawLevelField(x, y);
8076       }
8077     }
8078
8079     if (MovDelay[x][y])         // wait some time before next movement
8080     {
8081       MovDelay[x][y]--;
8082
8083       if (element == EL_ROBOT ||
8084           element == EL_YAMYAM ||
8085           element == EL_DARK_YAMYAM)
8086       {
8087         DrawLevelElementAnimationIfNeeded(x, y, element);
8088         PlayLevelSoundAction(x, y, ACTION_WAITING);
8089       }
8090       else if (element == EL_SP_ELECTRON)
8091         DrawLevelElementAnimationIfNeeded(x, y, element);
8092       else if (element == EL_DRAGON)
8093       {
8094         int i;
8095         int dir = MovDir[x][y];
8096         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8097         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8098         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8099                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8100                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8101                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8102         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8103
8104         GfxAction[x][y] = ACTION_ATTACKING;
8105
8106         if (IS_PLAYER(x, y))
8107           DrawPlayerField(x, y);
8108         else
8109           TEST_DrawLevelField(x, y);
8110
8111         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8112
8113         for (i = 1; i <= 3; i++)
8114         {
8115           int xx = x + i * dx;
8116           int yy = y + i * dy;
8117           int sx = SCREENX(xx);
8118           int sy = SCREENY(yy);
8119           int flame_graphic = graphic + (i - 1);
8120
8121           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8122             break;
8123
8124           if (MovDelay[x][y])
8125           {
8126             int flamed = MovingOrBlocked2Element(xx, yy);
8127
8128             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8129               Bang(xx, yy);
8130             else
8131               RemoveMovingField(xx, yy);
8132
8133             ChangeDelay[xx][yy] = 0;
8134
8135             Tile[xx][yy] = EL_FLAMES;
8136
8137             if (IN_SCR_FIELD(sx, sy))
8138             {
8139               TEST_DrawLevelFieldCrumbled(xx, yy);
8140               DrawGraphic(sx, sy, flame_graphic, frame);
8141             }
8142           }
8143           else
8144           {
8145             if (Tile[xx][yy] == EL_FLAMES)
8146               Tile[xx][yy] = EL_EMPTY;
8147             TEST_DrawLevelField(xx, yy);
8148           }
8149         }
8150       }
8151
8152       if (MovDelay[x][y])       // element still has to wait some time
8153       {
8154         PlayLevelSoundAction(x, y, ACTION_WAITING);
8155
8156         return;
8157       }
8158     }
8159
8160     // now make next step
8161
8162     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8163
8164     if (DONT_COLLIDE_WITH(element) &&
8165         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8166         !PLAYER_ENEMY_PROTECTED(newx, newy))
8167     {
8168       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8169
8170       return;
8171     }
8172
8173     else if (CAN_MOVE_INTO_ACID(element) &&
8174              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8175              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8176              (MovDir[x][y] == MV_DOWN ||
8177               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8178     {
8179       SplashAcid(newx, newy);
8180       Store[x][y] = EL_ACID;
8181     }
8182     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8183     {
8184       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8185           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8186           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8187           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8188       {
8189         RemoveField(x, y);
8190         TEST_DrawLevelField(x, y);
8191
8192         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8193         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8194           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8195
8196         game.friends_still_needed--;
8197         if (!game.friends_still_needed &&
8198             !game.GameOver &&
8199             game.all_players_gone)
8200           LevelSolved();
8201
8202         return;
8203       }
8204       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8205       {
8206         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8207           TEST_DrawLevelField(newx, newy);
8208         else
8209           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8210       }
8211       else if (!IS_FREE(newx, newy))
8212       {
8213         GfxAction[x][y] = ACTION_WAITING;
8214
8215         if (IS_PLAYER(x, y))
8216           DrawPlayerField(x, y);
8217         else
8218           TEST_DrawLevelField(x, y);
8219
8220         return;
8221       }
8222     }
8223     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8224     {
8225       if (IS_FOOD_PIG(Tile[newx][newy]))
8226       {
8227         if (IS_MOVING(newx, newy))
8228           RemoveMovingField(newx, newy);
8229         else
8230         {
8231           Tile[newx][newy] = EL_EMPTY;
8232           TEST_DrawLevelField(newx, newy);
8233         }
8234
8235         PlayLevelSound(x, y, SND_PIG_DIGGING);
8236       }
8237       else if (!IS_FREE(newx, newy))
8238       {
8239         if (IS_PLAYER(x, y))
8240           DrawPlayerField(x, y);
8241         else
8242           TEST_DrawLevelField(x, y);
8243
8244         return;
8245       }
8246     }
8247     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8248     {
8249       if (Store[x][y] != EL_EMPTY)
8250       {
8251         boolean can_clone = FALSE;
8252         int xx, yy;
8253
8254         // check if element to clone is still there
8255         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8256         {
8257           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8258           {
8259             can_clone = TRUE;
8260
8261             break;
8262           }
8263         }
8264
8265         // cannot clone or target field not free anymore -- do not clone
8266         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8267           Store[x][y] = EL_EMPTY;
8268       }
8269
8270       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8271       {
8272         if (IS_MV_DIAGONAL(MovDir[x][y]))
8273         {
8274           int diagonal_move_dir = MovDir[x][y];
8275           int stored = Store[x][y];
8276           int change_delay = 8;
8277           int graphic;
8278
8279           // android is moving diagonally
8280
8281           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8282
8283           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8284           GfxElement[x][y] = EL_EMC_ANDROID;
8285           GfxAction[x][y] = ACTION_SHRINKING;
8286           GfxDir[x][y] = diagonal_move_dir;
8287           ChangeDelay[x][y] = change_delay;
8288
8289           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8290                                    GfxDir[x][y]);
8291
8292           DrawLevelGraphicAnimation(x, y, graphic);
8293           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8294
8295           if (Tile[newx][newy] == EL_ACID)
8296           {
8297             SplashAcid(newx, newy);
8298
8299             return;
8300           }
8301
8302           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8303
8304           Store[newx][newy] = EL_EMC_ANDROID;
8305           GfxElement[newx][newy] = EL_EMC_ANDROID;
8306           GfxAction[newx][newy] = ACTION_GROWING;
8307           GfxDir[newx][newy] = diagonal_move_dir;
8308           ChangeDelay[newx][newy] = change_delay;
8309
8310           graphic = el_act_dir2img(GfxElement[newx][newy],
8311                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8312
8313           DrawLevelGraphicAnimation(newx, newy, graphic);
8314           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8315
8316           return;
8317         }
8318         else
8319         {
8320           Tile[newx][newy] = EL_EMPTY;
8321           TEST_DrawLevelField(newx, newy);
8322
8323           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8324         }
8325       }
8326       else if (!IS_FREE(newx, newy))
8327       {
8328         return;
8329       }
8330     }
8331     else if (IS_CUSTOM_ELEMENT(element) &&
8332              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8333     {
8334       if (!DigFieldByCE(newx, newy, element))
8335         return;
8336
8337       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8338       {
8339         RunnerVisit[x][y] = FrameCounter;
8340         PlayerVisit[x][y] /= 8;         // expire player visit path
8341       }
8342     }
8343     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8344     {
8345       if (!IS_FREE(newx, newy))
8346       {
8347         if (IS_PLAYER(x, y))
8348           DrawPlayerField(x, y);
8349         else
8350           TEST_DrawLevelField(x, y);
8351
8352         return;
8353       }
8354       else
8355       {
8356         boolean wanna_flame = !RND(10);
8357         int dx = newx - x, dy = newy - y;
8358         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8359         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8360         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8361                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8362         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8363                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8364
8365         if ((wanna_flame ||
8366              IS_CLASSIC_ENEMY(element1) ||
8367              IS_CLASSIC_ENEMY(element2)) &&
8368             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8369             element1 != EL_FLAMES && element2 != EL_FLAMES)
8370         {
8371           ResetGfxAnimation(x, y);
8372           GfxAction[x][y] = ACTION_ATTACKING;
8373
8374           if (IS_PLAYER(x, y))
8375             DrawPlayerField(x, y);
8376           else
8377             TEST_DrawLevelField(x, y);
8378
8379           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8380
8381           MovDelay[x][y] = 50;
8382
8383           Tile[newx][newy] = EL_FLAMES;
8384           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8385             Tile[newx1][newy1] = EL_FLAMES;
8386           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8387             Tile[newx2][newy2] = EL_FLAMES;
8388
8389           return;
8390         }
8391       }
8392     }
8393     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8394              Tile[newx][newy] == EL_DIAMOND)
8395     {
8396       if (IS_MOVING(newx, newy))
8397         RemoveMovingField(newx, newy);
8398       else
8399       {
8400         Tile[newx][newy] = EL_EMPTY;
8401         TEST_DrawLevelField(newx, newy);
8402       }
8403
8404       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8405     }
8406     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8407              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8408     {
8409       if (AmoebaNr[newx][newy])
8410       {
8411         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8412         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8413             Tile[newx][newy] == EL_BD_AMOEBA)
8414           AmoebaCnt[AmoebaNr[newx][newy]]--;
8415       }
8416
8417       if (IS_MOVING(newx, newy))
8418       {
8419         RemoveMovingField(newx, newy);
8420       }
8421       else
8422       {
8423         Tile[newx][newy] = EL_EMPTY;
8424         TEST_DrawLevelField(newx, newy);
8425       }
8426
8427       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8428     }
8429     else if ((element == EL_PACMAN || element == EL_MOLE)
8430              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8431     {
8432       if (AmoebaNr[newx][newy])
8433       {
8434         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8435         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8436             Tile[newx][newy] == EL_BD_AMOEBA)
8437           AmoebaCnt[AmoebaNr[newx][newy]]--;
8438       }
8439
8440       if (element == EL_MOLE)
8441       {
8442         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8443         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8444
8445         ResetGfxAnimation(x, y);
8446         GfxAction[x][y] = ACTION_DIGGING;
8447         TEST_DrawLevelField(x, y);
8448
8449         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8450
8451         return;                         // wait for shrinking amoeba
8452       }
8453       else      // element == EL_PACMAN
8454       {
8455         Tile[newx][newy] = EL_EMPTY;
8456         TEST_DrawLevelField(newx, newy);
8457         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8458       }
8459     }
8460     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8461              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8462               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8463     {
8464       // wait for shrinking amoeba to completely disappear
8465       return;
8466     }
8467     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8468     {
8469       // object was running against a wall
8470
8471       TurnRound(x, y);
8472
8473       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8474         DrawLevelElementAnimation(x, y, element);
8475
8476       if (DONT_TOUCH(element))
8477         TestIfBadThingTouchesPlayer(x, y);
8478
8479       return;
8480     }
8481
8482     InitMovingField(x, y, MovDir[x][y]);
8483
8484     PlayLevelSoundAction(x, y, ACTION_MOVING);
8485   }
8486
8487   if (MovDir[x][y])
8488     ContinueMoving(x, y);
8489 }
8490
8491 void ContinueMoving(int x, int y)
8492 {
8493   int element = Tile[x][y];
8494   struct ElementInfo *ei = &element_info[element];
8495   int direction = MovDir[x][y];
8496   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8497   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8498   int newx = x + dx, newy = y + dy;
8499   int stored = Store[x][y];
8500   int stored_new = Store[newx][newy];
8501   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8502   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8503   boolean last_line = (newy == lev_fieldy - 1);
8504
8505   MovPos[x][y] += getElementMoveStepsize(x, y);
8506
8507   if (pushed_by_player) // special case: moving object pushed by player
8508     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8509
8510   if (ABS(MovPos[x][y]) < TILEX)
8511   {
8512     TEST_DrawLevelField(x, y);
8513
8514     return;     // element is still moving
8515   }
8516
8517   // element reached destination field
8518
8519   Tile[x][y] = EL_EMPTY;
8520   Tile[newx][newy] = element;
8521   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8522
8523   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8524   {
8525     element = Tile[newx][newy] = EL_ACID;
8526   }
8527   else if (element == EL_MOLE)
8528   {
8529     Tile[x][y] = EL_SAND;
8530
8531     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8532   }
8533   else if (element == EL_QUICKSAND_FILLING)
8534   {
8535     element = Tile[newx][newy] = get_next_element(element);
8536     Store[newx][newy] = Store[x][y];
8537   }
8538   else if (element == EL_QUICKSAND_EMPTYING)
8539   {
8540     Tile[x][y] = get_next_element(element);
8541     element = Tile[newx][newy] = Store[x][y];
8542   }
8543   else if (element == EL_QUICKSAND_FAST_FILLING)
8544   {
8545     element = Tile[newx][newy] = get_next_element(element);
8546     Store[newx][newy] = Store[x][y];
8547   }
8548   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8549   {
8550     Tile[x][y] = get_next_element(element);
8551     element = Tile[newx][newy] = Store[x][y];
8552   }
8553   else if (element == EL_MAGIC_WALL_FILLING)
8554   {
8555     element = Tile[newx][newy] = get_next_element(element);
8556     if (!game.magic_wall_active)
8557       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8558     Store[newx][newy] = Store[x][y];
8559   }
8560   else if (element == EL_MAGIC_WALL_EMPTYING)
8561   {
8562     Tile[x][y] = get_next_element(element);
8563     if (!game.magic_wall_active)
8564       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8565     element = Tile[newx][newy] = Store[x][y];
8566
8567     InitField(newx, newy, FALSE);
8568   }
8569   else if (element == EL_BD_MAGIC_WALL_FILLING)
8570   {
8571     element = Tile[newx][newy] = get_next_element(element);
8572     if (!game.magic_wall_active)
8573       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8574     Store[newx][newy] = Store[x][y];
8575   }
8576   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8577   {
8578     Tile[x][y] = get_next_element(element);
8579     if (!game.magic_wall_active)
8580       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8581     element = Tile[newx][newy] = Store[x][y];
8582
8583     InitField(newx, newy, FALSE);
8584   }
8585   else if (element == EL_DC_MAGIC_WALL_FILLING)
8586   {
8587     element = Tile[newx][newy] = get_next_element(element);
8588     if (!game.magic_wall_active)
8589       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8590     Store[newx][newy] = Store[x][y];
8591   }
8592   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8593   {
8594     Tile[x][y] = get_next_element(element);
8595     if (!game.magic_wall_active)
8596       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8597     element = Tile[newx][newy] = Store[x][y];
8598
8599     InitField(newx, newy, FALSE);
8600   }
8601   else if (element == EL_AMOEBA_DROPPING)
8602   {
8603     Tile[x][y] = get_next_element(element);
8604     element = Tile[newx][newy] = Store[x][y];
8605   }
8606   else if (element == EL_SOKOBAN_OBJECT)
8607   {
8608     if (Back[x][y])
8609       Tile[x][y] = Back[x][y];
8610
8611     if (Back[newx][newy])
8612       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8613
8614     Back[x][y] = Back[newx][newy] = 0;
8615   }
8616
8617   Store[x][y] = EL_EMPTY;
8618   MovPos[x][y] = 0;
8619   MovDir[x][y] = 0;
8620   MovDelay[x][y] = 0;
8621
8622   MovDelay[newx][newy] = 0;
8623
8624   if (CAN_CHANGE_OR_HAS_ACTION(element))
8625   {
8626     // copy element change control values to new field
8627     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8628     ChangePage[newx][newy]  = ChangePage[x][y];
8629     ChangeCount[newx][newy] = ChangeCount[x][y];
8630     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8631   }
8632
8633   CustomValue[newx][newy] = CustomValue[x][y];
8634
8635   ChangeDelay[x][y] = 0;
8636   ChangePage[x][y] = -1;
8637   ChangeCount[x][y] = 0;
8638   ChangeEvent[x][y] = -1;
8639
8640   CustomValue[x][y] = 0;
8641
8642   // copy animation control values to new field
8643   GfxFrame[newx][newy]  = GfxFrame[x][y];
8644   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8645   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8646   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8647
8648   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8649
8650   // some elements can leave other elements behind after moving
8651   if (ei->move_leave_element != EL_EMPTY &&
8652       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8653       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8654   {
8655     int move_leave_element = ei->move_leave_element;
8656
8657     // this makes it possible to leave the removed element again
8658     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8659       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8660
8661     Tile[x][y] = move_leave_element;
8662
8663     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8664       MovDir[x][y] = direction;
8665
8666     InitField(x, y, FALSE);
8667
8668     if (GFX_CRUMBLED(Tile[x][y]))
8669       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8670
8671     if (ELEM_IS_PLAYER(move_leave_element))
8672       RelocatePlayer(x, y, move_leave_element);
8673   }
8674
8675   // do this after checking for left-behind element
8676   ResetGfxAnimation(x, y);      // reset animation values for old field
8677
8678   if (!CAN_MOVE(element) ||
8679       (CAN_FALL(element) && direction == MV_DOWN &&
8680        (element == EL_SPRING ||
8681         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8682         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8683     GfxDir[x][y] = MovDir[newx][newy] = 0;
8684
8685   TEST_DrawLevelField(x, y);
8686   TEST_DrawLevelField(newx, newy);
8687
8688   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8689
8690   // prevent pushed element from moving on in pushed direction
8691   if (pushed_by_player && CAN_MOVE(element) &&
8692       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8693       !(element_info[element].move_pattern & direction))
8694     TurnRound(newx, newy);
8695
8696   // prevent elements on conveyor belt from moving on in last direction
8697   if (pushed_by_conveyor && CAN_FALL(element) &&
8698       direction & MV_HORIZONTAL)
8699     MovDir[newx][newy] = 0;
8700
8701   if (!pushed_by_player)
8702   {
8703     int nextx = newx + dx, nexty = newy + dy;
8704     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8705
8706     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8707
8708     if (CAN_FALL(element) && direction == MV_DOWN)
8709       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8710
8711     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8712       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8713
8714     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8715       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8716   }
8717
8718   if (DONT_TOUCH(element))      // object may be nasty to player or others
8719   {
8720     TestIfBadThingTouchesPlayer(newx, newy);
8721     TestIfBadThingTouchesFriend(newx, newy);
8722
8723     if (!IS_CUSTOM_ELEMENT(element))
8724       TestIfBadThingTouchesOtherBadThing(newx, newy);
8725   }
8726   else if (element == EL_PENGUIN)
8727     TestIfFriendTouchesBadThing(newx, newy);
8728
8729   if (DONT_GET_HIT_BY(element))
8730   {
8731     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8732   }
8733
8734   // give the player one last chance (one more frame) to move away
8735   if (CAN_FALL(element) && direction == MV_DOWN &&
8736       (last_line || (!IS_FREE(x, newy + 1) &&
8737                      (!IS_PLAYER(x, newy + 1) ||
8738                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8739     Impact(x, newy);
8740
8741   if (pushed_by_player && !game.use_change_when_pushing_bug)
8742   {
8743     int push_side = MV_DIR_OPPOSITE(direction);
8744     struct PlayerInfo *player = PLAYERINFO(x, y);
8745
8746     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8747                                player->index_bit, push_side);
8748     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8749                                         player->index_bit, push_side);
8750   }
8751
8752   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8753     MovDelay[newx][newy] = 1;
8754
8755   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8756
8757   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8758   TestIfElementHitsCustomElement(newx, newy, direction);
8759   TestIfPlayerTouchesCustomElement(newx, newy);
8760   TestIfElementTouchesCustomElement(newx, newy);
8761
8762   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8763       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8764     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8765                              MV_DIR_OPPOSITE(direction));
8766 }
8767
8768 int AmoebaNeighbourNr(int ax, int ay)
8769 {
8770   int i;
8771   int element = Tile[ax][ay];
8772   int group_nr = 0;
8773   static int xy[4][2] =
8774   {
8775     { 0, -1 },
8776     { -1, 0 },
8777     { +1, 0 },
8778     { 0, +1 }
8779   };
8780
8781   for (i = 0; i < NUM_DIRECTIONS; i++)
8782   {
8783     int x = ax + xy[i][0];
8784     int y = ay + xy[i][1];
8785
8786     if (!IN_LEV_FIELD(x, y))
8787       continue;
8788
8789     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8790       group_nr = AmoebaNr[x][y];
8791   }
8792
8793   return group_nr;
8794 }
8795
8796 static void AmoebaMerge(int ax, int ay)
8797 {
8798   int i, x, y, xx, yy;
8799   int new_group_nr = AmoebaNr[ax][ay];
8800   static int xy[4][2] =
8801   {
8802     { 0, -1 },
8803     { -1, 0 },
8804     { +1, 0 },
8805     { 0, +1 }
8806   };
8807
8808   if (new_group_nr == 0)
8809     return;
8810
8811   for (i = 0; i < NUM_DIRECTIONS; i++)
8812   {
8813     x = ax + xy[i][0];
8814     y = ay + xy[i][1];
8815
8816     if (!IN_LEV_FIELD(x, y))
8817       continue;
8818
8819     if ((Tile[x][y] == EL_AMOEBA_FULL ||
8820          Tile[x][y] == EL_BD_AMOEBA ||
8821          Tile[x][y] == EL_AMOEBA_DEAD) &&
8822         AmoebaNr[x][y] != new_group_nr)
8823     {
8824       int old_group_nr = AmoebaNr[x][y];
8825
8826       if (old_group_nr == 0)
8827         return;
8828
8829       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8830       AmoebaCnt[old_group_nr] = 0;
8831       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8832       AmoebaCnt2[old_group_nr] = 0;
8833
8834       SCAN_PLAYFIELD(xx, yy)
8835       {
8836         if (AmoebaNr[xx][yy] == old_group_nr)
8837           AmoebaNr[xx][yy] = new_group_nr;
8838       }
8839     }
8840   }
8841 }
8842
8843 void AmoebaToDiamond(int ax, int ay)
8844 {
8845   int i, x, y;
8846
8847   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
8848   {
8849     int group_nr = AmoebaNr[ax][ay];
8850
8851 #ifdef DEBUG
8852     if (group_nr == 0)
8853     {
8854       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
8855       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
8856
8857       return;
8858     }
8859 #endif
8860
8861     SCAN_PLAYFIELD(x, y)
8862     {
8863       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8864       {
8865         AmoebaNr[x][y] = 0;
8866         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
8867       }
8868     }
8869
8870     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8871                             SND_AMOEBA_TURNING_TO_GEM :
8872                             SND_AMOEBA_TURNING_TO_ROCK));
8873     Bang(ax, ay);
8874   }
8875   else
8876   {
8877     static int xy[4][2] =
8878     {
8879       { 0, -1 },
8880       { -1, 0 },
8881       { +1, 0 },
8882       { 0, +1 }
8883     };
8884
8885     for (i = 0; i < NUM_DIRECTIONS; i++)
8886     {
8887       x = ax + xy[i][0];
8888       y = ay + xy[i][1];
8889
8890       if (!IN_LEV_FIELD(x, y))
8891         continue;
8892
8893       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
8894       {
8895         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8896                               SND_AMOEBA_TURNING_TO_GEM :
8897                               SND_AMOEBA_TURNING_TO_ROCK));
8898         Bang(x, y);
8899       }
8900     }
8901   }
8902 }
8903
8904 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
8905 {
8906   int x, y;
8907   int group_nr = AmoebaNr[ax][ay];
8908   boolean done = FALSE;
8909
8910 #ifdef DEBUG
8911   if (group_nr == 0)
8912   {
8913     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
8914     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
8915
8916     return;
8917   }
8918 #endif
8919
8920   SCAN_PLAYFIELD(x, y)
8921   {
8922     if (AmoebaNr[x][y] == group_nr &&
8923         (Tile[x][y] == EL_AMOEBA_DEAD ||
8924          Tile[x][y] == EL_BD_AMOEBA ||
8925          Tile[x][y] == EL_AMOEBA_GROWING))
8926     {
8927       AmoebaNr[x][y] = 0;
8928       Tile[x][y] = new_element;
8929       InitField(x, y, FALSE);
8930       TEST_DrawLevelField(x, y);
8931       done = TRUE;
8932     }
8933   }
8934
8935   if (done)
8936     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8937                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8938                             SND_BD_AMOEBA_TURNING_TO_GEM));
8939 }
8940
8941 static void AmoebaGrowing(int x, int y)
8942 {
8943   static unsigned int sound_delay = 0;
8944   static unsigned int sound_delay_value = 0;
8945
8946   if (!MovDelay[x][y])          // start new growing cycle
8947   {
8948     MovDelay[x][y] = 7;
8949
8950     if (DelayReached(&sound_delay, sound_delay_value))
8951     {
8952       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8953       sound_delay_value = 30;
8954     }
8955   }
8956
8957   if (MovDelay[x][y])           // wait some time before growing bigger
8958   {
8959     MovDelay[x][y]--;
8960     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8961     {
8962       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8963                                            6 - MovDelay[x][y]);
8964
8965       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8966     }
8967
8968     if (!MovDelay[x][y])
8969     {
8970       Tile[x][y] = Store[x][y];
8971       Store[x][y] = 0;
8972       TEST_DrawLevelField(x, y);
8973     }
8974   }
8975 }
8976
8977 static void AmoebaShrinking(int x, int y)
8978 {
8979   static unsigned int sound_delay = 0;
8980   static unsigned int sound_delay_value = 0;
8981
8982   if (!MovDelay[x][y])          // start new shrinking cycle
8983   {
8984     MovDelay[x][y] = 7;
8985
8986     if (DelayReached(&sound_delay, sound_delay_value))
8987       sound_delay_value = 30;
8988   }
8989
8990   if (MovDelay[x][y])           // wait some time before shrinking
8991   {
8992     MovDelay[x][y]--;
8993     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8994     {
8995       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8996                                            6 - MovDelay[x][y]);
8997
8998       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8999     }
9000
9001     if (!MovDelay[x][y])
9002     {
9003       Tile[x][y] = EL_EMPTY;
9004       TEST_DrawLevelField(x, y);
9005
9006       // don't let mole enter this field in this cycle;
9007       // (give priority to objects falling to this field from above)
9008       Stop[x][y] = TRUE;
9009     }
9010   }
9011 }
9012
9013 static void AmoebaReproduce(int ax, int ay)
9014 {
9015   int i;
9016   int element = Tile[ax][ay];
9017   int graphic = el2img(element);
9018   int newax = ax, neway = ay;
9019   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9020   static int xy[4][2] =
9021   {
9022     { 0, -1 },
9023     { -1, 0 },
9024     { +1, 0 },
9025     { 0, +1 }
9026   };
9027
9028   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9029   {
9030     Tile[ax][ay] = EL_AMOEBA_DEAD;
9031     TEST_DrawLevelField(ax, ay);
9032     return;
9033   }
9034
9035   if (IS_ANIMATED(graphic))
9036     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9037
9038   if (!MovDelay[ax][ay])        // start making new amoeba field
9039     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9040
9041   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9042   {
9043     MovDelay[ax][ay]--;
9044     if (MovDelay[ax][ay])
9045       return;
9046   }
9047
9048   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9049   {
9050     int start = RND(4);
9051     int x = ax + xy[start][0];
9052     int y = ay + xy[start][1];
9053
9054     if (!IN_LEV_FIELD(x, y))
9055       return;
9056
9057     if (IS_FREE(x, y) ||
9058         CAN_GROW_INTO(Tile[x][y]) ||
9059         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9060         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9061     {
9062       newax = x;
9063       neway = y;
9064     }
9065
9066     if (newax == ax && neway == ay)
9067       return;
9068   }
9069   else                          // normal or "filled" (BD style) amoeba
9070   {
9071     int start = RND(4);
9072     boolean waiting_for_player = FALSE;
9073
9074     for (i = 0; i < NUM_DIRECTIONS; i++)
9075     {
9076       int j = (start + i) % 4;
9077       int x = ax + xy[j][0];
9078       int y = ay + xy[j][1];
9079
9080       if (!IN_LEV_FIELD(x, y))
9081         continue;
9082
9083       if (IS_FREE(x, y) ||
9084           CAN_GROW_INTO(Tile[x][y]) ||
9085           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9086           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9087       {
9088         newax = x;
9089         neway = y;
9090         break;
9091       }
9092       else if (IS_PLAYER(x, y))
9093         waiting_for_player = TRUE;
9094     }
9095
9096     if (newax == ax && neway == ay)             // amoeba cannot grow
9097     {
9098       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9099       {
9100         Tile[ax][ay] = EL_AMOEBA_DEAD;
9101         TEST_DrawLevelField(ax, ay);
9102         AmoebaCnt[AmoebaNr[ax][ay]]--;
9103
9104         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9105         {
9106           if (element == EL_AMOEBA_FULL)
9107             AmoebaToDiamond(ax, ay);
9108           else if (element == EL_BD_AMOEBA)
9109             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9110         }
9111       }
9112       return;
9113     }
9114     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9115     {
9116       // amoeba gets larger by growing in some direction
9117
9118       int new_group_nr = AmoebaNr[ax][ay];
9119
9120 #ifdef DEBUG
9121   if (new_group_nr == 0)
9122   {
9123     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9124           newax, neway);
9125     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9126
9127     return;
9128   }
9129 #endif
9130
9131       AmoebaNr[newax][neway] = new_group_nr;
9132       AmoebaCnt[new_group_nr]++;
9133       AmoebaCnt2[new_group_nr]++;
9134
9135       // if amoeba touches other amoeba(s) after growing, unify them
9136       AmoebaMerge(newax, neway);
9137
9138       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9139       {
9140         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9141         return;
9142       }
9143     }
9144   }
9145
9146   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9147       (neway == lev_fieldy - 1 && newax != ax))
9148   {
9149     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9150     Store[newax][neway] = element;
9151   }
9152   else if (neway == ay || element == EL_EMC_DRIPPER)
9153   {
9154     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9155
9156     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9157   }
9158   else
9159   {
9160     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9161     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9162     Store[ax][ay] = EL_AMOEBA_DROP;
9163     ContinueMoving(ax, ay);
9164     return;
9165   }
9166
9167   TEST_DrawLevelField(newax, neway);
9168 }
9169
9170 static void Life(int ax, int ay)
9171 {
9172   int x1, y1, x2, y2;
9173   int life_time = 40;
9174   int element = Tile[ax][ay];
9175   int graphic = el2img(element);
9176   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9177                          level.biomaze);
9178   boolean changed = FALSE;
9179
9180   if (IS_ANIMATED(graphic))
9181     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9182
9183   if (Stop[ax][ay])
9184     return;
9185
9186   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9187     MovDelay[ax][ay] = life_time;
9188
9189   if (MovDelay[ax][ay])         // wait some time before next cycle
9190   {
9191     MovDelay[ax][ay]--;
9192     if (MovDelay[ax][ay])
9193       return;
9194   }
9195
9196   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9197   {
9198     int xx = ax+x1, yy = ay+y1;
9199     int old_element = Tile[xx][yy];
9200     int num_neighbours = 0;
9201
9202     if (!IN_LEV_FIELD(xx, yy))
9203       continue;
9204
9205     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9206     {
9207       int x = xx+x2, y = yy+y2;
9208
9209       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9210         continue;
9211
9212       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9213       boolean is_neighbour = FALSE;
9214
9215       if (level.use_life_bugs)
9216         is_neighbour =
9217           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9218            (IS_FREE(x, y)                             &&  Stop[x][y]));
9219       else
9220         is_neighbour =
9221           (Last[x][y] == element || is_player_cell);
9222
9223       if (is_neighbour)
9224         num_neighbours++;
9225     }
9226
9227     boolean is_free = FALSE;
9228
9229     if (level.use_life_bugs)
9230       is_free = (IS_FREE(xx, yy));
9231     else
9232       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9233
9234     if (xx == ax && yy == ay)           // field in the middle
9235     {
9236       if (num_neighbours < life_parameter[0] ||
9237           num_neighbours > life_parameter[1])
9238       {
9239         Tile[xx][yy] = EL_EMPTY;
9240         if (Tile[xx][yy] != old_element)
9241           TEST_DrawLevelField(xx, yy);
9242         Stop[xx][yy] = TRUE;
9243         changed = TRUE;
9244       }
9245     }
9246     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9247     {                                   // free border field
9248       if (num_neighbours >= life_parameter[2] &&
9249           num_neighbours <= life_parameter[3])
9250       {
9251         Tile[xx][yy] = element;
9252         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9253         if (Tile[xx][yy] != old_element)
9254           TEST_DrawLevelField(xx, yy);
9255         Stop[xx][yy] = TRUE;
9256         changed = TRUE;
9257       }
9258     }
9259   }
9260
9261   if (changed)
9262     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9263                    SND_GAME_OF_LIFE_GROWING);
9264 }
9265
9266 static void InitRobotWheel(int x, int y)
9267 {
9268   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9269 }
9270
9271 static void RunRobotWheel(int x, int y)
9272 {
9273   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9274 }
9275
9276 static void StopRobotWheel(int x, int y)
9277 {
9278   if (game.robot_wheel_x == x &&
9279       game.robot_wheel_y == y)
9280   {
9281     game.robot_wheel_x = -1;
9282     game.robot_wheel_y = -1;
9283     game.robot_wheel_active = FALSE;
9284   }
9285 }
9286
9287 static void InitTimegateWheel(int x, int y)
9288 {
9289   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9290 }
9291
9292 static void RunTimegateWheel(int x, int y)
9293 {
9294   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9295 }
9296
9297 static void InitMagicBallDelay(int x, int y)
9298 {
9299   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9300 }
9301
9302 static void ActivateMagicBall(int bx, int by)
9303 {
9304   int x, y;
9305
9306   if (level.ball_random)
9307   {
9308     int pos_border = RND(8);    // select one of the eight border elements
9309     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9310     int xx = pos_content % 3;
9311     int yy = pos_content / 3;
9312
9313     x = bx - 1 + xx;
9314     y = by - 1 + yy;
9315
9316     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9317       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9318   }
9319   else
9320   {
9321     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9322     {
9323       int xx = x - bx + 1;
9324       int yy = y - by + 1;
9325
9326       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9327         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9328     }
9329   }
9330
9331   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9332 }
9333
9334 static void CheckExit(int x, int y)
9335 {
9336   if (game.gems_still_needed > 0 ||
9337       game.sokoban_fields_still_needed > 0 ||
9338       game.sokoban_objects_still_needed > 0 ||
9339       game.lights_still_needed > 0)
9340   {
9341     int element = Tile[x][y];
9342     int graphic = el2img(element);
9343
9344     if (IS_ANIMATED(graphic))
9345       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9346
9347     return;
9348   }
9349
9350   // do not re-open exit door closed after last player
9351   if (game.all_players_gone)
9352     return;
9353
9354   Tile[x][y] = EL_EXIT_OPENING;
9355
9356   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9357 }
9358
9359 static void CheckExitEM(int x, int y)
9360 {
9361   if (game.gems_still_needed > 0 ||
9362       game.sokoban_fields_still_needed > 0 ||
9363       game.sokoban_objects_still_needed > 0 ||
9364       game.lights_still_needed > 0)
9365   {
9366     int element = Tile[x][y];
9367     int graphic = el2img(element);
9368
9369     if (IS_ANIMATED(graphic))
9370       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9371
9372     return;
9373   }
9374
9375   // do not re-open exit door closed after last player
9376   if (game.all_players_gone)
9377     return;
9378
9379   Tile[x][y] = EL_EM_EXIT_OPENING;
9380
9381   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9382 }
9383
9384 static void CheckExitSteel(int x, int y)
9385 {
9386   if (game.gems_still_needed > 0 ||
9387       game.sokoban_fields_still_needed > 0 ||
9388       game.sokoban_objects_still_needed > 0 ||
9389       game.lights_still_needed > 0)
9390   {
9391     int element = Tile[x][y];
9392     int graphic = el2img(element);
9393
9394     if (IS_ANIMATED(graphic))
9395       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9396
9397     return;
9398   }
9399
9400   // do not re-open exit door closed after last player
9401   if (game.all_players_gone)
9402     return;
9403
9404   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9405
9406   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9407 }
9408
9409 static void CheckExitSteelEM(int x, int y)
9410 {
9411   if (game.gems_still_needed > 0 ||
9412       game.sokoban_fields_still_needed > 0 ||
9413       game.sokoban_objects_still_needed > 0 ||
9414       game.lights_still_needed > 0)
9415   {
9416     int element = Tile[x][y];
9417     int graphic = el2img(element);
9418
9419     if (IS_ANIMATED(graphic))
9420       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9421
9422     return;
9423   }
9424
9425   // do not re-open exit door closed after last player
9426   if (game.all_players_gone)
9427     return;
9428
9429   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9430
9431   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9432 }
9433
9434 static void CheckExitSP(int x, int y)
9435 {
9436   if (game.gems_still_needed > 0)
9437   {
9438     int element = Tile[x][y];
9439     int graphic = el2img(element);
9440
9441     if (IS_ANIMATED(graphic))
9442       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9443
9444     return;
9445   }
9446
9447   // do not re-open exit door closed after last player
9448   if (game.all_players_gone)
9449     return;
9450
9451   Tile[x][y] = EL_SP_EXIT_OPENING;
9452
9453   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9454 }
9455
9456 static void CloseAllOpenTimegates(void)
9457 {
9458   int x, y;
9459
9460   SCAN_PLAYFIELD(x, y)
9461   {
9462     int element = Tile[x][y];
9463
9464     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9465     {
9466       Tile[x][y] = EL_TIMEGATE_CLOSING;
9467
9468       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9469     }
9470   }
9471 }
9472
9473 static void DrawTwinkleOnField(int x, int y)
9474 {
9475   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9476     return;
9477
9478   if (Tile[x][y] == EL_BD_DIAMOND)
9479     return;
9480
9481   if (MovDelay[x][y] == 0)      // next animation frame
9482     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9483
9484   if (MovDelay[x][y] != 0)      // wait some time before next frame
9485   {
9486     MovDelay[x][y]--;
9487
9488     DrawLevelElementAnimation(x, y, Tile[x][y]);
9489
9490     if (MovDelay[x][y] != 0)
9491     {
9492       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9493                                            10 - MovDelay[x][y]);
9494
9495       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9496     }
9497   }
9498 }
9499
9500 static void MauerWaechst(int x, int y)
9501 {
9502   int delay = 6;
9503
9504   if (!MovDelay[x][y])          // next animation frame
9505     MovDelay[x][y] = 3 * delay;
9506
9507   if (MovDelay[x][y])           // wait some time before next frame
9508   {
9509     MovDelay[x][y]--;
9510
9511     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9512     {
9513       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9514       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9515
9516       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9517     }
9518
9519     if (!MovDelay[x][y])
9520     {
9521       if (MovDir[x][y] == MV_LEFT)
9522       {
9523         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9524           TEST_DrawLevelField(x - 1, y);
9525       }
9526       else if (MovDir[x][y] == MV_RIGHT)
9527       {
9528         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9529           TEST_DrawLevelField(x + 1, y);
9530       }
9531       else if (MovDir[x][y] == MV_UP)
9532       {
9533         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9534           TEST_DrawLevelField(x, y - 1);
9535       }
9536       else
9537       {
9538         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9539           TEST_DrawLevelField(x, y + 1);
9540       }
9541
9542       Tile[x][y] = Store[x][y];
9543       Store[x][y] = 0;
9544       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9545       TEST_DrawLevelField(x, y);
9546     }
9547   }
9548 }
9549
9550 static void MauerAbleger(int ax, int ay)
9551 {
9552   int element = Tile[ax][ay];
9553   int graphic = el2img(element);
9554   boolean oben_frei = FALSE, unten_frei = FALSE;
9555   boolean links_frei = FALSE, rechts_frei = FALSE;
9556   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9557   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9558   boolean new_wall = FALSE;
9559
9560   if (IS_ANIMATED(graphic))
9561     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9562
9563   if (!MovDelay[ax][ay])        // start building new wall
9564     MovDelay[ax][ay] = 6;
9565
9566   if (MovDelay[ax][ay])         // wait some time before building new wall
9567   {
9568     MovDelay[ax][ay]--;
9569     if (MovDelay[ax][ay])
9570       return;
9571   }
9572
9573   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9574     oben_frei = TRUE;
9575   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9576     unten_frei = TRUE;
9577   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9578     links_frei = TRUE;
9579   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9580     rechts_frei = TRUE;
9581
9582   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9583       element == EL_EXPANDABLE_WALL_ANY)
9584   {
9585     if (oben_frei)
9586     {
9587       Tile[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9588       Store[ax][ay-1] = element;
9589       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9590       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9591         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9592                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9593       new_wall = TRUE;
9594     }
9595     if (unten_frei)
9596     {
9597       Tile[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9598       Store[ax][ay+1] = element;
9599       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9600       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9601         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9602                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9603       new_wall = TRUE;
9604     }
9605   }
9606
9607   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9608       element == EL_EXPANDABLE_WALL_ANY ||
9609       element == EL_EXPANDABLE_WALL ||
9610       element == EL_BD_EXPANDABLE_WALL)
9611   {
9612     if (links_frei)
9613     {
9614       Tile[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9615       Store[ax-1][ay] = element;
9616       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9617       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9618         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9619                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9620       new_wall = TRUE;
9621     }
9622
9623     if (rechts_frei)
9624     {
9625       Tile[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9626       Store[ax+1][ay] = element;
9627       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9628       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9629         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9630                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9631       new_wall = TRUE;
9632     }
9633   }
9634
9635   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9636     TEST_DrawLevelField(ax, ay);
9637
9638   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9639     oben_massiv = TRUE;
9640   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9641     unten_massiv = TRUE;
9642   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9643     links_massiv = TRUE;
9644   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9645     rechts_massiv = TRUE;
9646
9647   if (((oben_massiv && unten_massiv) ||
9648        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9649        element == EL_EXPANDABLE_WALL) &&
9650       ((links_massiv && rechts_massiv) ||
9651        element == EL_EXPANDABLE_WALL_VERTICAL))
9652     Tile[ax][ay] = EL_WALL;
9653
9654   if (new_wall)
9655     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9656 }
9657
9658 static void MauerAblegerStahl(int ax, int ay)
9659 {
9660   int element = Tile[ax][ay];
9661   int graphic = el2img(element);
9662   boolean oben_frei = FALSE, unten_frei = FALSE;
9663   boolean links_frei = FALSE, rechts_frei = FALSE;
9664   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9665   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9666   boolean new_wall = FALSE;
9667
9668   if (IS_ANIMATED(graphic))
9669     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9670
9671   if (!MovDelay[ax][ay])        // start building new wall
9672     MovDelay[ax][ay] = 6;
9673
9674   if (MovDelay[ax][ay])         // wait some time before building new wall
9675   {
9676     MovDelay[ax][ay]--;
9677     if (MovDelay[ax][ay])
9678       return;
9679   }
9680
9681   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9682     oben_frei = TRUE;
9683   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9684     unten_frei = TRUE;
9685   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9686     links_frei = TRUE;
9687   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9688     rechts_frei = TRUE;
9689
9690   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9691       element == EL_EXPANDABLE_STEELWALL_ANY)
9692   {
9693     if (oben_frei)
9694     {
9695       Tile[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9696       Store[ax][ay-1] = element;
9697       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9698       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9699         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9700                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9701       new_wall = TRUE;
9702     }
9703     if (unten_frei)
9704     {
9705       Tile[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9706       Store[ax][ay+1] = element;
9707       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9708       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9709         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9710                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9711       new_wall = TRUE;
9712     }
9713   }
9714
9715   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9716       element == EL_EXPANDABLE_STEELWALL_ANY)
9717   {
9718     if (links_frei)
9719     {
9720       Tile[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9721       Store[ax-1][ay] = element;
9722       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9723       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9724         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9725                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9726       new_wall = TRUE;
9727     }
9728
9729     if (rechts_frei)
9730     {
9731       Tile[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9732       Store[ax+1][ay] = element;
9733       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9734       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9735         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9736                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9737       new_wall = TRUE;
9738     }
9739   }
9740
9741   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9742     oben_massiv = TRUE;
9743   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9744     unten_massiv = TRUE;
9745   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9746     links_massiv = TRUE;
9747   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9748     rechts_massiv = TRUE;
9749
9750   if (((oben_massiv && unten_massiv) ||
9751        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9752       ((links_massiv && rechts_massiv) ||
9753        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9754     Tile[ax][ay] = EL_STEELWALL;
9755
9756   if (new_wall)
9757     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9758 }
9759
9760 static void CheckForDragon(int x, int y)
9761 {
9762   int i, j;
9763   boolean dragon_found = FALSE;
9764   static int xy[4][2] =
9765   {
9766     { 0, -1 },
9767     { -1, 0 },
9768     { +1, 0 },
9769     { 0, +1 }
9770   };
9771
9772   for (i = 0; i < NUM_DIRECTIONS; i++)
9773   {
9774     for (j = 0; j < 4; j++)
9775     {
9776       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9777
9778       if (IN_LEV_FIELD(xx, yy) &&
9779           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9780       {
9781         if (Tile[xx][yy] == EL_DRAGON)
9782           dragon_found = TRUE;
9783       }
9784       else
9785         break;
9786     }
9787   }
9788
9789   if (!dragon_found)
9790   {
9791     for (i = 0; i < NUM_DIRECTIONS; i++)
9792     {
9793       for (j = 0; j < 3; j++)
9794       {
9795         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9796   
9797         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9798         {
9799           Tile[xx][yy] = EL_EMPTY;
9800           TEST_DrawLevelField(xx, yy);
9801         }
9802         else
9803           break;
9804       }
9805     }
9806   }
9807 }
9808
9809 static void InitBuggyBase(int x, int y)
9810 {
9811   int element = Tile[x][y];
9812   int activating_delay = FRAMES_PER_SECOND / 4;
9813
9814   ChangeDelay[x][y] =
9815     (element == EL_SP_BUGGY_BASE ?
9816      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9817      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9818      activating_delay :
9819      element == EL_SP_BUGGY_BASE_ACTIVE ?
9820      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9821 }
9822
9823 static void WarnBuggyBase(int x, int y)
9824 {
9825   int i;
9826   static int xy[4][2] =
9827   {
9828     { 0, -1 },
9829     { -1, 0 },
9830     { +1, 0 },
9831     { 0, +1 }
9832   };
9833
9834   for (i = 0; i < NUM_DIRECTIONS; i++)
9835   {
9836     int xx = x + xy[i][0];
9837     int yy = y + xy[i][1];
9838
9839     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9840     {
9841       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9842
9843       break;
9844     }
9845   }
9846 }
9847
9848 static void InitTrap(int x, int y)
9849 {
9850   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9851 }
9852
9853 static void ActivateTrap(int x, int y)
9854 {
9855   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9856 }
9857
9858 static void ChangeActiveTrap(int x, int y)
9859 {
9860   int graphic = IMG_TRAP_ACTIVE;
9861
9862   // if new animation frame was drawn, correct crumbled sand border
9863   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9864     TEST_DrawLevelFieldCrumbled(x, y);
9865 }
9866
9867 static int getSpecialActionElement(int element, int number, int base_element)
9868 {
9869   return (element != EL_EMPTY ? element :
9870           number != -1 ? base_element + number - 1 :
9871           EL_EMPTY);
9872 }
9873
9874 static int getModifiedActionNumber(int value_old, int operator, int operand,
9875                                    int value_min, int value_max)
9876 {
9877   int value_new = (operator == CA_MODE_SET      ? operand :
9878                    operator == CA_MODE_ADD      ? value_old + operand :
9879                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9880                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9881                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9882                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9883                    value_old);
9884
9885   return (value_new < value_min ? value_min :
9886           value_new > value_max ? value_max :
9887           value_new);
9888 }
9889
9890 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9891 {
9892   struct ElementInfo *ei = &element_info[element];
9893   struct ElementChangeInfo *change = &ei->change_page[page];
9894   int target_element = change->target_element;
9895   int action_type = change->action_type;
9896   int action_mode = change->action_mode;
9897   int action_arg = change->action_arg;
9898   int action_element = change->action_element;
9899   int i;
9900
9901   if (!change->has_action)
9902     return;
9903
9904   // ---------- determine action paramater values -----------------------------
9905
9906   int level_time_value =
9907     (level.time > 0 ? TimeLeft :
9908      TimePlayed);
9909
9910   int action_arg_element_raw =
9911     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9912      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9913      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9914      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9915      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9916      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9917      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9918      EL_EMPTY);
9919   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9920
9921   int action_arg_direction =
9922     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9923      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9924      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9925      change->actual_trigger_side :
9926      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9927      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9928      MV_NONE);
9929
9930   int action_arg_number_min =
9931     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9932      CA_ARG_MIN);
9933
9934   int action_arg_number_max =
9935     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9936      action_type == CA_SET_LEVEL_GEMS ? 999 :
9937      action_type == CA_SET_LEVEL_TIME ? 9999 :
9938      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9939      action_type == CA_SET_CE_VALUE ? 9999 :
9940      action_type == CA_SET_CE_SCORE ? 9999 :
9941      CA_ARG_MAX);
9942
9943   int action_arg_number_reset =
9944     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9945      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9946      action_type == CA_SET_LEVEL_TIME ? level.time :
9947      action_type == CA_SET_LEVEL_SCORE ? 0 :
9948      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9949      action_type == CA_SET_CE_SCORE ? 0 :
9950      0);
9951
9952   int action_arg_number =
9953     (action_arg <= CA_ARG_MAX ? action_arg :
9954      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9955      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9956      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9957      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9958      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9959      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9960      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9961      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9962      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9963      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9964      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
9965      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
9966      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9967      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9968      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9969      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9970      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9971      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9972      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9973      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9974      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9975      -1);
9976
9977   int action_arg_number_old =
9978     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
9979      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9980      action_type == CA_SET_LEVEL_SCORE ? game.score :
9981      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9982      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9983      0);
9984
9985   int action_arg_number_new =
9986     getModifiedActionNumber(action_arg_number_old,
9987                             action_mode, action_arg_number,
9988                             action_arg_number_min, action_arg_number_max);
9989
9990   int trigger_player_bits =
9991     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9992      change->actual_trigger_player_bits : change->trigger_player);
9993
9994   int action_arg_player_bits =
9995     (action_arg >= CA_ARG_PLAYER_1 &&
9996      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9997      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9998      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9999      PLAYER_BITS_ANY);
10000
10001   // ---------- execute action  -----------------------------------------------
10002
10003   switch (action_type)
10004   {
10005     case CA_NO_ACTION:
10006     {
10007       return;
10008     }
10009
10010     // ---------- level actions  ----------------------------------------------
10011
10012     case CA_RESTART_LEVEL:
10013     {
10014       game.restart_level = TRUE;
10015
10016       break;
10017     }
10018
10019     case CA_SHOW_ENVELOPE:
10020     {
10021       int element = getSpecialActionElement(action_arg_element,
10022                                             action_arg_number, EL_ENVELOPE_1);
10023
10024       if (IS_ENVELOPE(element))
10025         local_player->show_envelope = element;
10026
10027       break;
10028     }
10029
10030     case CA_SET_LEVEL_TIME:
10031     {
10032       if (level.time > 0)       // only modify limited time value
10033       {
10034         TimeLeft = action_arg_number_new;
10035
10036         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10037
10038         DisplayGameControlValues();
10039
10040         if (!TimeLeft && setup.time_limit)
10041           for (i = 0; i < MAX_PLAYERS; i++)
10042             KillPlayer(&stored_player[i]);
10043       }
10044
10045       break;
10046     }
10047
10048     case CA_SET_LEVEL_SCORE:
10049     {
10050       game.score = action_arg_number_new;
10051
10052       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10053
10054       DisplayGameControlValues();
10055
10056       break;
10057     }
10058
10059     case CA_SET_LEVEL_GEMS:
10060     {
10061       game.gems_still_needed = action_arg_number_new;
10062
10063       game.snapshot.collected_item = TRUE;
10064
10065       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10066
10067       DisplayGameControlValues();
10068
10069       break;
10070     }
10071
10072     case CA_SET_LEVEL_WIND:
10073     {
10074       game.wind_direction = action_arg_direction;
10075
10076       break;
10077     }
10078
10079     case CA_SET_LEVEL_RANDOM_SEED:
10080     {
10081       // ensure that setting a new random seed while playing is predictable
10082       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10083
10084       break;
10085     }
10086
10087     // ---------- player actions  ---------------------------------------------
10088
10089     case CA_MOVE_PLAYER:
10090     case CA_MOVE_PLAYER_NEW:
10091     {
10092       // automatically move to the next field in specified direction
10093       for (i = 0; i < MAX_PLAYERS; i++)
10094         if (trigger_player_bits & (1 << i))
10095           if (action_type == CA_MOVE_PLAYER ||
10096               stored_player[i].MovPos == 0)
10097             stored_player[i].programmed_action = action_arg_direction;
10098
10099       break;
10100     }
10101
10102     case CA_EXIT_PLAYER:
10103     {
10104       for (i = 0; i < MAX_PLAYERS; i++)
10105         if (action_arg_player_bits & (1 << i))
10106           ExitPlayer(&stored_player[i]);
10107
10108       if (game.players_still_needed == 0)
10109         LevelSolved();
10110
10111       break;
10112     }
10113
10114     case CA_KILL_PLAYER:
10115     {
10116       for (i = 0; i < MAX_PLAYERS; i++)
10117         if (action_arg_player_bits & (1 << i))
10118           KillPlayer(&stored_player[i]);
10119
10120       break;
10121     }
10122
10123     case CA_SET_PLAYER_KEYS:
10124     {
10125       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10126       int element = getSpecialActionElement(action_arg_element,
10127                                             action_arg_number, EL_KEY_1);
10128
10129       if (IS_KEY(element))
10130       {
10131         for (i = 0; i < MAX_PLAYERS; i++)
10132         {
10133           if (trigger_player_bits & (1 << i))
10134           {
10135             stored_player[i].key[KEY_NR(element)] = key_state;
10136
10137             DrawGameDoorValues();
10138           }
10139         }
10140       }
10141
10142       break;
10143     }
10144
10145     case CA_SET_PLAYER_SPEED:
10146     {
10147       for (i = 0; i < MAX_PLAYERS; i++)
10148       {
10149         if (trigger_player_bits & (1 << i))
10150         {
10151           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10152
10153           if (action_arg == CA_ARG_SPEED_FASTER &&
10154               stored_player[i].cannot_move)
10155           {
10156             action_arg_number = STEPSIZE_VERY_SLOW;
10157           }
10158           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10159                    action_arg == CA_ARG_SPEED_FASTER)
10160           {
10161             action_arg_number = 2;
10162             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10163                            CA_MODE_MULTIPLY);
10164           }
10165           else if (action_arg == CA_ARG_NUMBER_RESET)
10166           {
10167             action_arg_number = level.initial_player_stepsize[i];
10168           }
10169
10170           move_stepsize =
10171             getModifiedActionNumber(move_stepsize,
10172                                     action_mode,
10173                                     action_arg_number,
10174                                     action_arg_number_min,
10175                                     action_arg_number_max);
10176
10177           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10178         }
10179       }
10180
10181       break;
10182     }
10183
10184     case CA_SET_PLAYER_SHIELD:
10185     {
10186       for (i = 0; i < MAX_PLAYERS; i++)
10187       {
10188         if (trigger_player_bits & (1 << i))
10189         {
10190           if (action_arg == CA_ARG_SHIELD_OFF)
10191           {
10192             stored_player[i].shield_normal_time_left = 0;
10193             stored_player[i].shield_deadly_time_left = 0;
10194           }
10195           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10196           {
10197             stored_player[i].shield_normal_time_left = 999999;
10198           }
10199           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10200           {
10201             stored_player[i].shield_normal_time_left = 999999;
10202             stored_player[i].shield_deadly_time_left = 999999;
10203           }
10204         }
10205       }
10206
10207       break;
10208     }
10209
10210     case CA_SET_PLAYER_GRAVITY:
10211     {
10212       for (i = 0; i < MAX_PLAYERS; i++)
10213       {
10214         if (trigger_player_bits & (1 << i))
10215         {
10216           stored_player[i].gravity =
10217             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10218              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10219              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10220              stored_player[i].gravity);
10221         }
10222       }
10223
10224       break;
10225     }
10226
10227     case CA_SET_PLAYER_ARTWORK:
10228     {
10229       for (i = 0; i < MAX_PLAYERS; i++)
10230       {
10231         if (trigger_player_bits & (1 << i))
10232         {
10233           int artwork_element = action_arg_element;
10234
10235           if (action_arg == CA_ARG_ELEMENT_RESET)
10236             artwork_element =
10237               (level.use_artwork_element[i] ? level.artwork_element[i] :
10238                stored_player[i].element_nr);
10239
10240           if (stored_player[i].artwork_element != artwork_element)
10241             stored_player[i].Frame = 0;
10242
10243           stored_player[i].artwork_element = artwork_element;
10244
10245           SetPlayerWaiting(&stored_player[i], FALSE);
10246
10247           // set number of special actions for bored and sleeping animation
10248           stored_player[i].num_special_action_bored =
10249             get_num_special_action(artwork_element,
10250                                    ACTION_BORING_1, ACTION_BORING_LAST);
10251           stored_player[i].num_special_action_sleeping =
10252             get_num_special_action(artwork_element,
10253                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10254         }
10255       }
10256
10257       break;
10258     }
10259
10260     case CA_SET_PLAYER_INVENTORY:
10261     {
10262       for (i = 0; i < MAX_PLAYERS; i++)
10263       {
10264         struct PlayerInfo *player = &stored_player[i];
10265         int j, k;
10266
10267         if (trigger_player_bits & (1 << i))
10268         {
10269           int inventory_element = action_arg_element;
10270
10271           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10272               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10273               action_arg == CA_ARG_ELEMENT_ACTION)
10274           {
10275             int element = inventory_element;
10276             int collect_count = element_info[element].collect_count_initial;
10277
10278             if (!IS_CUSTOM_ELEMENT(element))
10279               collect_count = 1;
10280
10281             if (collect_count == 0)
10282               player->inventory_infinite_element = element;
10283             else
10284               for (k = 0; k < collect_count; k++)
10285                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10286                   player->inventory_element[player->inventory_size++] =
10287                     element;
10288           }
10289           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10290                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10291                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10292           {
10293             if (player->inventory_infinite_element != EL_UNDEFINED &&
10294                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10295                                      action_arg_element_raw))
10296               player->inventory_infinite_element = EL_UNDEFINED;
10297
10298             for (k = 0, j = 0; j < player->inventory_size; j++)
10299             {
10300               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10301                                         action_arg_element_raw))
10302                 player->inventory_element[k++] = player->inventory_element[j];
10303             }
10304
10305             player->inventory_size = k;
10306           }
10307           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10308           {
10309             if (player->inventory_size > 0)
10310             {
10311               for (j = 0; j < player->inventory_size - 1; j++)
10312                 player->inventory_element[j] = player->inventory_element[j + 1];
10313
10314               player->inventory_size--;
10315             }
10316           }
10317           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10318           {
10319             if (player->inventory_size > 0)
10320               player->inventory_size--;
10321           }
10322           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10323           {
10324             player->inventory_infinite_element = EL_UNDEFINED;
10325             player->inventory_size = 0;
10326           }
10327           else if (action_arg == CA_ARG_INVENTORY_RESET)
10328           {
10329             player->inventory_infinite_element = EL_UNDEFINED;
10330             player->inventory_size = 0;
10331
10332             if (level.use_initial_inventory[i])
10333             {
10334               for (j = 0; j < level.initial_inventory_size[i]; j++)
10335               {
10336                 int element = level.initial_inventory_content[i][j];
10337                 int collect_count = element_info[element].collect_count_initial;
10338
10339                 if (!IS_CUSTOM_ELEMENT(element))
10340                   collect_count = 1;
10341
10342                 if (collect_count == 0)
10343                   player->inventory_infinite_element = element;
10344                 else
10345                   for (k = 0; k < collect_count; k++)
10346                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10347                       player->inventory_element[player->inventory_size++] =
10348                         element;
10349               }
10350             }
10351           }
10352         }
10353       }
10354
10355       break;
10356     }
10357
10358     // ---------- CE actions  -------------------------------------------------
10359
10360     case CA_SET_CE_VALUE:
10361     {
10362       int last_ce_value = CustomValue[x][y];
10363
10364       CustomValue[x][y] = action_arg_number_new;
10365
10366       if (CustomValue[x][y] != last_ce_value)
10367       {
10368         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10369         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10370
10371         if (CustomValue[x][y] == 0)
10372         {
10373           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10374           ChangeCount[x][y] = 0;        // allow at least one more change
10375
10376           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10377           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10378         }
10379       }
10380
10381       break;
10382     }
10383
10384     case CA_SET_CE_SCORE:
10385     {
10386       int last_ce_score = ei->collect_score;
10387
10388       ei->collect_score = action_arg_number_new;
10389
10390       if (ei->collect_score != last_ce_score)
10391       {
10392         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10393         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10394
10395         if (ei->collect_score == 0)
10396         {
10397           int xx, yy;
10398
10399           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10400           ChangeCount[x][y] = 0;        // allow at least one more change
10401
10402           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10403           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10404
10405           /*
10406             This is a very special case that seems to be a mixture between
10407             CheckElementChange() and CheckTriggeredElementChange(): while
10408             the first one only affects single elements that are triggered
10409             directly, the second one affects multiple elements in the playfield
10410             that are triggered indirectly by another element. This is a third
10411             case: Changing the CE score always affects multiple identical CEs,
10412             so every affected CE must be checked, not only the single CE for
10413             which the CE score was changed in the first place (as every instance
10414             of that CE shares the same CE score, and therefore also can change)!
10415           */
10416           SCAN_PLAYFIELD(xx, yy)
10417           {
10418             if (Tile[xx][yy] == element)
10419               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10420                                  CE_SCORE_GETS_ZERO);
10421           }
10422         }
10423       }
10424
10425       break;
10426     }
10427
10428     case CA_SET_CE_ARTWORK:
10429     {
10430       int artwork_element = action_arg_element;
10431       boolean reset_frame = FALSE;
10432       int xx, yy;
10433
10434       if (action_arg == CA_ARG_ELEMENT_RESET)
10435         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10436                            element);
10437
10438       if (ei->gfx_element != artwork_element)
10439         reset_frame = TRUE;
10440
10441       ei->gfx_element = artwork_element;
10442
10443       SCAN_PLAYFIELD(xx, yy)
10444       {
10445         if (Tile[xx][yy] == element)
10446         {
10447           if (reset_frame)
10448           {
10449             ResetGfxAnimation(xx, yy);
10450             ResetRandomAnimationValue(xx, yy);
10451           }
10452
10453           TEST_DrawLevelField(xx, yy);
10454         }
10455       }
10456
10457       break;
10458     }
10459
10460     // ---------- engine actions  ---------------------------------------------
10461
10462     case CA_SET_ENGINE_SCAN_MODE:
10463     {
10464       InitPlayfieldScanMode(action_arg);
10465
10466       break;
10467     }
10468
10469     default:
10470       break;
10471   }
10472 }
10473
10474 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10475 {
10476   int old_element = Tile[x][y];
10477   int new_element = GetElementFromGroupElement(element);
10478   int previous_move_direction = MovDir[x][y];
10479   int last_ce_value = CustomValue[x][y];
10480   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10481   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10482   boolean add_player_onto_element = (new_element_is_player &&
10483                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10484                                      IS_WALKABLE(old_element));
10485
10486   if (!add_player_onto_element)
10487   {
10488     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10489       RemoveMovingField(x, y);
10490     else
10491       RemoveField(x, y);
10492
10493     Tile[x][y] = new_element;
10494
10495     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10496       MovDir[x][y] = previous_move_direction;
10497
10498     if (element_info[new_element].use_last_ce_value)
10499       CustomValue[x][y] = last_ce_value;
10500
10501     InitField_WithBug1(x, y, FALSE);
10502
10503     new_element = Tile[x][y];   // element may have changed
10504
10505     ResetGfxAnimation(x, y);
10506     ResetRandomAnimationValue(x, y);
10507
10508     TEST_DrawLevelField(x, y);
10509
10510     if (GFX_CRUMBLED(new_element))
10511       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10512   }
10513
10514   // check if element under the player changes from accessible to unaccessible
10515   // (needed for special case of dropping element which then changes)
10516   // (must be checked after creating new element for walkable group elements)
10517   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10518       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10519   {
10520     Bang(x, y);
10521
10522     return;
10523   }
10524
10525   // "ChangeCount" not set yet to allow "entered by player" change one time
10526   if (new_element_is_player)
10527     RelocatePlayer(x, y, new_element);
10528
10529   if (is_change)
10530     ChangeCount[x][y]++;        // count number of changes in the same frame
10531
10532   TestIfBadThingTouchesPlayer(x, y);
10533   TestIfPlayerTouchesCustomElement(x, y);
10534   TestIfElementTouchesCustomElement(x, y);
10535 }
10536
10537 static void CreateField(int x, int y, int element)
10538 {
10539   CreateFieldExt(x, y, element, FALSE);
10540 }
10541
10542 static void CreateElementFromChange(int x, int y, int element)
10543 {
10544   element = GET_VALID_RUNTIME_ELEMENT(element);
10545
10546   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10547   {
10548     int old_element = Tile[x][y];
10549
10550     // prevent changed element from moving in same engine frame
10551     // unless both old and new element can either fall or move
10552     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10553         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10554       Stop[x][y] = TRUE;
10555   }
10556
10557   CreateFieldExt(x, y, element, TRUE);
10558 }
10559
10560 static boolean ChangeElement(int x, int y, int element, int page)
10561 {
10562   struct ElementInfo *ei = &element_info[element];
10563   struct ElementChangeInfo *change = &ei->change_page[page];
10564   int ce_value = CustomValue[x][y];
10565   int ce_score = ei->collect_score;
10566   int target_element;
10567   int old_element = Tile[x][y];
10568
10569   // always use default change event to prevent running into a loop
10570   if (ChangeEvent[x][y] == -1)
10571     ChangeEvent[x][y] = CE_DELAY;
10572
10573   if (ChangeEvent[x][y] == CE_DELAY)
10574   {
10575     // reset actual trigger element, trigger player and action element
10576     change->actual_trigger_element = EL_EMPTY;
10577     change->actual_trigger_player = EL_EMPTY;
10578     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10579     change->actual_trigger_side = CH_SIDE_NONE;
10580     change->actual_trigger_ce_value = 0;
10581     change->actual_trigger_ce_score = 0;
10582   }
10583
10584   // do not change elements more than a specified maximum number of changes
10585   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10586     return FALSE;
10587
10588   ChangeCount[x][y]++;          // count number of changes in the same frame
10589
10590   if (change->explode)
10591   {
10592     Bang(x, y);
10593
10594     return TRUE;
10595   }
10596
10597   if (change->use_target_content)
10598   {
10599     boolean complete_replace = TRUE;
10600     boolean can_replace[3][3];
10601     int xx, yy;
10602
10603     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10604     {
10605       boolean is_empty;
10606       boolean is_walkable;
10607       boolean is_diggable;
10608       boolean is_collectible;
10609       boolean is_removable;
10610       boolean is_destructible;
10611       int ex = x + xx - 1;
10612       int ey = y + yy - 1;
10613       int content_element = change->target_content.e[xx][yy];
10614       int e;
10615
10616       can_replace[xx][yy] = TRUE;
10617
10618       if (ex == x && ey == y)   // do not check changing element itself
10619         continue;
10620
10621       if (content_element == EL_EMPTY_SPACE)
10622       {
10623         can_replace[xx][yy] = FALSE;    // do not replace border with space
10624
10625         continue;
10626       }
10627
10628       if (!IN_LEV_FIELD(ex, ey))
10629       {
10630         can_replace[xx][yy] = FALSE;
10631         complete_replace = FALSE;
10632
10633         continue;
10634       }
10635
10636       e = Tile[ex][ey];
10637
10638       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10639         e = MovingOrBlocked2Element(ex, ey);
10640
10641       is_empty = (IS_FREE(ex, ey) ||
10642                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10643
10644       is_walkable     = (is_empty || IS_WALKABLE(e));
10645       is_diggable     = (is_empty || IS_DIGGABLE(e));
10646       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10647       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10648       is_removable    = (is_diggable || is_collectible);
10649
10650       can_replace[xx][yy] =
10651         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10652           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10653           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10654           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10655           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10656           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10657          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10658
10659       if (!can_replace[xx][yy])
10660         complete_replace = FALSE;
10661     }
10662
10663     if (!change->only_if_complete || complete_replace)
10664     {
10665       boolean something_has_changed = FALSE;
10666
10667       if (change->only_if_complete && change->use_random_replace &&
10668           RND(100) < change->random_percentage)
10669         return FALSE;
10670
10671       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10672       {
10673         int ex = x + xx - 1;
10674         int ey = y + yy - 1;
10675         int content_element;
10676
10677         if (can_replace[xx][yy] && (!change->use_random_replace ||
10678                                     RND(100) < change->random_percentage))
10679         {
10680           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10681             RemoveMovingField(ex, ey);
10682
10683           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10684
10685           content_element = change->target_content.e[xx][yy];
10686           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10687                                               ce_value, ce_score);
10688
10689           CreateElementFromChange(ex, ey, target_element);
10690
10691           something_has_changed = TRUE;
10692
10693           // for symmetry reasons, freeze newly created border elements
10694           if (ex != x || ey != y)
10695             Stop[ex][ey] = TRUE;        // no more moving in this frame
10696         }
10697       }
10698
10699       if (something_has_changed)
10700       {
10701         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10702         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10703       }
10704     }
10705   }
10706   else
10707   {
10708     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10709                                         ce_value, ce_score);
10710
10711     if (element == EL_DIAGONAL_GROWING ||
10712         element == EL_DIAGONAL_SHRINKING)
10713     {
10714       target_element = Store[x][y];
10715
10716       Store[x][y] = EL_EMPTY;
10717     }
10718
10719     CreateElementFromChange(x, y, target_element);
10720
10721     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10722     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10723   }
10724
10725   // this uses direct change before indirect change
10726   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10727
10728   return TRUE;
10729 }
10730
10731 static void HandleElementChange(int x, int y, int page)
10732 {
10733   int element = MovingOrBlocked2Element(x, y);
10734   struct ElementInfo *ei = &element_info[element];
10735   struct ElementChangeInfo *change = &ei->change_page[page];
10736   boolean handle_action_before_change = FALSE;
10737
10738 #ifdef DEBUG
10739   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10740       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10741   {
10742     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10743           x, y, element, element_info[element].token_name);
10744     Debug("game:playing:HandleElementChange", "This should never happen!");
10745   }
10746 #endif
10747
10748   // this can happen with classic bombs on walkable, changing elements
10749   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10750   {
10751     return;
10752   }
10753
10754   if (ChangeDelay[x][y] == 0)           // initialize element change
10755   {
10756     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10757
10758     if (change->can_change)
10759     {
10760       // !!! not clear why graphic animation should be reset at all here !!!
10761       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10762       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10763
10764       /*
10765         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10766
10767         When using an animation frame delay of 1 (this only happens with
10768         "sp_zonk.moving.left/right" in the classic graphics), the default
10769         (non-moving) animation shows wrong animation frames (while the
10770         moving animation, like "sp_zonk.moving.left/right", is correct,
10771         so this graphical bug never shows up with the classic graphics).
10772         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10773         be drawn instead of the correct frames 0,1,2,3. This is caused by
10774         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10775         an element change: First when the change delay ("ChangeDelay[][]")
10776         counter has reached zero after decrementing, then a second time in
10777         the next frame (after "GfxFrame[][]" was already incremented) when
10778         "ChangeDelay[][]" is reset to the initial delay value again.
10779
10780         This causes frame 0 to be drawn twice, while the last frame won't
10781         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10782
10783         As some animations may already be cleverly designed around this bug
10784         (at least the "Snake Bite" snake tail animation does this), it cannot
10785         simply be fixed here without breaking such existing animations.
10786         Unfortunately, it cannot easily be detected if a graphics set was
10787         designed "before" or "after" the bug was fixed. As a workaround,
10788         a new graphics set option "game.graphics_engine_version" was added
10789         to be able to specify the game's major release version for which the
10790         graphics set was designed, which can then be used to decide if the
10791         bugfix should be used (version 4 and above) or not (version 3 or
10792         below, or if no version was specified at all, as with old sets).
10793
10794         (The wrong/fixed animation frames can be tested with the test level set
10795         "test_gfxframe" and level "000", which contains a specially prepared
10796         custom element at level position (x/y) == (11/9) which uses the zonk
10797         animation mentioned above. Using "game.graphics_engine_version: 4"
10798         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10799         This can also be seen from the debug output for this test element.)
10800       */
10801
10802       // when a custom element is about to change (for example by change delay),
10803       // do not reset graphic animation when the custom element is moving
10804       if (game.graphics_engine_version < 4 &&
10805           !IS_MOVING(x, y))
10806       {
10807         ResetGfxAnimation(x, y);
10808         ResetRandomAnimationValue(x, y);
10809       }
10810
10811       if (change->pre_change_function)
10812         change->pre_change_function(x, y);
10813     }
10814   }
10815
10816   ChangeDelay[x][y]--;
10817
10818   if (ChangeDelay[x][y] != 0)           // continue element change
10819   {
10820     if (change->can_change)
10821     {
10822       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10823
10824       if (IS_ANIMATED(graphic))
10825         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10826
10827       if (change->change_function)
10828         change->change_function(x, y);
10829     }
10830   }
10831   else                                  // finish element change
10832   {
10833     if (ChangePage[x][y] != -1)         // remember page from delayed change
10834     {
10835       page = ChangePage[x][y];
10836       ChangePage[x][y] = -1;
10837
10838       change = &ei->change_page[page];
10839     }
10840
10841     if (IS_MOVING(x, y))                // never change a running system ;-)
10842     {
10843       ChangeDelay[x][y] = 1;            // try change after next move step
10844       ChangePage[x][y] = page;          // remember page to use for change
10845
10846       return;
10847     }
10848
10849     // special case: set new level random seed before changing element
10850     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10851       handle_action_before_change = TRUE;
10852
10853     if (change->has_action && handle_action_before_change)
10854       ExecuteCustomElementAction(x, y, element, page);
10855
10856     if (change->can_change)
10857     {
10858       if (ChangeElement(x, y, element, page))
10859       {
10860         if (change->post_change_function)
10861           change->post_change_function(x, y);
10862       }
10863     }
10864
10865     if (change->has_action && !handle_action_before_change)
10866       ExecuteCustomElementAction(x, y, element, page);
10867   }
10868 }
10869
10870 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10871                                               int trigger_element,
10872                                               int trigger_event,
10873                                               int trigger_player,
10874                                               int trigger_side,
10875                                               int trigger_page)
10876 {
10877   boolean change_done_any = FALSE;
10878   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10879   int i;
10880
10881   if (!(trigger_events[trigger_element][trigger_event]))
10882     return FALSE;
10883
10884   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10885
10886   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10887   {
10888     int element = EL_CUSTOM_START + i;
10889     boolean change_done = FALSE;
10890     int p;
10891
10892     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10893         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10894       continue;
10895
10896     for (p = 0; p < element_info[element].num_change_pages; p++)
10897     {
10898       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10899
10900       if (change->can_change_or_has_action &&
10901           change->has_event[trigger_event] &&
10902           change->trigger_side & trigger_side &&
10903           change->trigger_player & trigger_player &&
10904           change->trigger_page & trigger_page_bits &&
10905           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10906       {
10907         change->actual_trigger_element = trigger_element;
10908         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10909         change->actual_trigger_player_bits = trigger_player;
10910         change->actual_trigger_side = trigger_side;
10911         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10912         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10913
10914         if ((change->can_change && !change_done) || change->has_action)
10915         {
10916           int x, y;
10917
10918           SCAN_PLAYFIELD(x, y)
10919           {
10920             if (Tile[x][y] == element)
10921             {
10922               if (change->can_change && !change_done)
10923               {
10924                 // if element already changed in this frame, not only prevent
10925                 // another element change (checked in ChangeElement()), but
10926                 // also prevent additional element actions for this element
10927
10928                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10929                     !level.use_action_after_change_bug)
10930                   continue;
10931
10932                 ChangeDelay[x][y] = 1;
10933                 ChangeEvent[x][y] = trigger_event;
10934
10935                 HandleElementChange(x, y, p);
10936               }
10937               else if (change->has_action)
10938               {
10939                 // if element already changed in this frame, not only prevent
10940                 // another element change (checked in ChangeElement()), but
10941                 // also prevent additional element actions for this element
10942
10943                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10944                     !level.use_action_after_change_bug)
10945                   continue;
10946
10947                 ExecuteCustomElementAction(x, y, element, p);
10948                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10949               }
10950             }
10951           }
10952
10953           if (change->can_change)
10954           {
10955             change_done = TRUE;
10956             change_done_any = TRUE;
10957           }
10958         }
10959       }
10960     }
10961   }
10962
10963   RECURSION_LOOP_DETECTION_END();
10964
10965   return change_done_any;
10966 }
10967
10968 static boolean CheckElementChangeExt(int x, int y,
10969                                      int element,
10970                                      int trigger_element,
10971                                      int trigger_event,
10972                                      int trigger_player,
10973                                      int trigger_side)
10974 {
10975   boolean change_done = FALSE;
10976   int p;
10977
10978   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10979       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10980     return FALSE;
10981
10982   if (Tile[x][y] == EL_BLOCKED)
10983   {
10984     Blocked2Moving(x, y, &x, &y);
10985     element = Tile[x][y];
10986   }
10987
10988   // check if element has already changed or is about to change after moving
10989   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10990        Tile[x][y] != element) ||
10991
10992       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10993        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10994         ChangePage[x][y] != -1)))
10995     return FALSE;
10996
10997   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10998
10999   for (p = 0; p < element_info[element].num_change_pages; p++)
11000   {
11001     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11002
11003     /* check trigger element for all events where the element that is checked
11004        for changing interacts with a directly adjacent element -- this is
11005        different to element changes that affect other elements to change on the
11006        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11007     boolean check_trigger_element =
11008       (trigger_event == CE_TOUCHING_X ||
11009        trigger_event == CE_HITTING_X ||
11010        trigger_event == CE_HIT_BY_X ||
11011        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11012
11013     if (change->can_change_or_has_action &&
11014         change->has_event[trigger_event] &&
11015         change->trigger_side & trigger_side &&
11016         change->trigger_player & trigger_player &&
11017         (!check_trigger_element ||
11018          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11019     {
11020       change->actual_trigger_element = trigger_element;
11021       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11022       change->actual_trigger_player_bits = trigger_player;
11023       change->actual_trigger_side = trigger_side;
11024       change->actual_trigger_ce_value = CustomValue[x][y];
11025       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11026
11027       // special case: trigger element not at (x,y) position for some events
11028       if (check_trigger_element)
11029       {
11030         static struct
11031         {
11032           int dx, dy;
11033         } move_xy[] =
11034           {
11035             {  0,  0 },
11036             { -1,  0 },
11037             { +1,  0 },
11038             {  0,  0 },
11039             {  0, -1 },
11040             {  0,  0 }, { 0, 0 }, { 0, 0 },
11041             {  0, +1 }
11042           };
11043
11044         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11045         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11046
11047         change->actual_trigger_ce_value = CustomValue[xx][yy];
11048         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11049       }
11050
11051       if (change->can_change && !change_done)
11052       {
11053         ChangeDelay[x][y] = 1;
11054         ChangeEvent[x][y] = trigger_event;
11055
11056         HandleElementChange(x, y, p);
11057
11058         change_done = TRUE;
11059       }
11060       else if (change->has_action)
11061       {
11062         ExecuteCustomElementAction(x, y, element, p);
11063         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11064       }
11065     }
11066   }
11067
11068   RECURSION_LOOP_DETECTION_END();
11069
11070   return change_done;
11071 }
11072
11073 static void PlayPlayerSound(struct PlayerInfo *player)
11074 {
11075   int jx = player->jx, jy = player->jy;
11076   int sound_element = player->artwork_element;
11077   int last_action = player->last_action_waiting;
11078   int action = player->action_waiting;
11079
11080   if (player->is_waiting)
11081   {
11082     if (action != last_action)
11083       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11084     else
11085       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11086   }
11087   else
11088   {
11089     if (action != last_action)
11090       StopSound(element_info[sound_element].sound[last_action]);
11091
11092     if (last_action == ACTION_SLEEPING)
11093       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11094   }
11095 }
11096
11097 static void PlayAllPlayersSound(void)
11098 {
11099   int i;
11100
11101   for (i = 0; i < MAX_PLAYERS; i++)
11102     if (stored_player[i].active)
11103       PlayPlayerSound(&stored_player[i]);
11104 }
11105
11106 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11107 {
11108   boolean last_waiting = player->is_waiting;
11109   int move_dir = player->MovDir;
11110
11111   player->dir_waiting = move_dir;
11112   player->last_action_waiting = player->action_waiting;
11113
11114   if (is_waiting)
11115   {
11116     if (!last_waiting)          // not waiting -> waiting
11117     {
11118       player->is_waiting = TRUE;
11119
11120       player->frame_counter_bored =
11121         FrameCounter +
11122         game.player_boring_delay_fixed +
11123         GetSimpleRandom(game.player_boring_delay_random);
11124       player->frame_counter_sleeping =
11125         FrameCounter +
11126         game.player_sleeping_delay_fixed +
11127         GetSimpleRandom(game.player_sleeping_delay_random);
11128
11129       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11130     }
11131
11132     if (game.player_sleeping_delay_fixed +
11133         game.player_sleeping_delay_random > 0 &&
11134         player->anim_delay_counter == 0 &&
11135         player->post_delay_counter == 0 &&
11136         FrameCounter >= player->frame_counter_sleeping)
11137       player->is_sleeping = TRUE;
11138     else if (game.player_boring_delay_fixed +
11139              game.player_boring_delay_random > 0 &&
11140              FrameCounter >= player->frame_counter_bored)
11141       player->is_bored = TRUE;
11142
11143     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11144                               player->is_bored ? ACTION_BORING :
11145                               ACTION_WAITING);
11146
11147     if (player->is_sleeping && player->use_murphy)
11148     {
11149       // special case for sleeping Murphy when leaning against non-free tile
11150
11151       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11152           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11153            !IS_MOVING(player->jx - 1, player->jy)))
11154         move_dir = MV_LEFT;
11155       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11156                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11157                 !IS_MOVING(player->jx + 1, player->jy)))
11158         move_dir = MV_RIGHT;
11159       else
11160         player->is_sleeping = FALSE;
11161
11162       player->dir_waiting = move_dir;
11163     }
11164
11165     if (player->is_sleeping)
11166     {
11167       if (player->num_special_action_sleeping > 0)
11168       {
11169         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11170         {
11171           int last_special_action = player->special_action_sleeping;
11172           int num_special_action = player->num_special_action_sleeping;
11173           int special_action =
11174             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11175              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11176              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11177              last_special_action + 1 : ACTION_SLEEPING);
11178           int special_graphic =
11179             el_act_dir2img(player->artwork_element, special_action, move_dir);
11180
11181           player->anim_delay_counter =
11182             graphic_info[special_graphic].anim_delay_fixed +
11183             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11184           player->post_delay_counter =
11185             graphic_info[special_graphic].post_delay_fixed +
11186             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11187
11188           player->special_action_sleeping = special_action;
11189         }
11190
11191         if (player->anim_delay_counter > 0)
11192         {
11193           player->action_waiting = player->special_action_sleeping;
11194           player->anim_delay_counter--;
11195         }
11196         else if (player->post_delay_counter > 0)
11197         {
11198           player->post_delay_counter--;
11199         }
11200       }
11201     }
11202     else if (player->is_bored)
11203     {
11204       if (player->num_special_action_bored > 0)
11205       {
11206         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11207         {
11208           int special_action =
11209             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11210           int special_graphic =
11211             el_act_dir2img(player->artwork_element, special_action, move_dir);
11212
11213           player->anim_delay_counter =
11214             graphic_info[special_graphic].anim_delay_fixed +
11215             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11216           player->post_delay_counter =
11217             graphic_info[special_graphic].post_delay_fixed +
11218             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11219
11220           player->special_action_bored = special_action;
11221         }
11222
11223         if (player->anim_delay_counter > 0)
11224         {
11225           player->action_waiting = player->special_action_bored;
11226           player->anim_delay_counter--;
11227         }
11228         else if (player->post_delay_counter > 0)
11229         {
11230           player->post_delay_counter--;
11231         }
11232       }
11233     }
11234   }
11235   else if (last_waiting)        // waiting -> not waiting
11236   {
11237     player->is_waiting = FALSE;
11238     player->is_bored = FALSE;
11239     player->is_sleeping = FALSE;
11240
11241     player->frame_counter_bored = -1;
11242     player->frame_counter_sleeping = -1;
11243
11244     player->anim_delay_counter = 0;
11245     player->post_delay_counter = 0;
11246
11247     player->dir_waiting = player->MovDir;
11248     player->action_waiting = ACTION_DEFAULT;
11249
11250     player->special_action_bored = ACTION_DEFAULT;
11251     player->special_action_sleeping = ACTION_DEFAULT;
11252   }
11253 }
11254
11255 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11256 {
11257   if ((!player->is_moving  && player->was_moving) ||
11258       (player->MovPos == 0 && player->was_moving) ||
11259       (player->is_snapping && !player->was_snapping) ||
11260       (player->is_dropping && !player->was_dropping))
11261   {
11262     if (!CheckSaveEngineSnapshotToList())
11263       return;
11264
11265     player->was_moving = FALSE;
11266     player->was_snapping = TRUE;
11267     player->was_dropping = TRUE;
11268   }
11269   else
11270   {
11271     if (player->is_moving)
11272       player->was_moving = TRUE;
11273
11274     if (!player->is_snapping)
11275       player->was_snapping = FALSE;
11276
11277     if (!player->is_dropping)
11278       player->was_dropping = FALSE;
11279   }
11280
11281   static struct MouseActionInfo mouse_action_last = { 0 };
11282   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11283   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11284
11285   if (new_released)
11286     CheckSaveEngineSnapshotToList();
11287
11288   mouse_action_last = mouse_action;
11289 }
11290
11291 static void CheckSingleStepMode(struct PlayerInfo *player)
11292 {
11293   if (tape.single_step && tape.recording && !tape.pausing)
11294   {
11295     // as it is called "single step mode", just return to pause mode when the
11296     // player stopped moving after one tile (or never starts moving at all)
11297     // (reverse logic needed here in case single step mode used in team mode)
11298     if (player->is_moving ||
11299         player->is_pushing ||
11300         player->is_dropping_pressed ||
11301         player->effective_mouse_action.button)
11302       game.enter_single_step_mode = FALSE;
11303   }
11304
11305   CheckSaveEngineSnapshot(player);
11306 }
11307
11308 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11309 {
11310   int left      = player_action & JOY_LEFT;
11311   int right     = player_action & JOY_RIGHT;
11312   int up        = player_action & JOY_UP;
11313   int down      = player_action & JOY_DOWN;
11314   int button1   = player_action & JOY_BUTTON_1;
11315   int button2   = player_action & JOY_BUTTON_2;
11316   int dx        = (left ? -1 : right ? 1 : 0);
11317   int dy        = (up   ? -1 : down  ? 1 : 0);
11318
11319   if (!player->active || tape.pausing)
11320     return 0;
11321
11322   if (player_action)
11323   {
11324     if (button1)
11325       SnapField(player, dx, dy);
11326     else
11327     {
11328       if (button2)
11329         DropElement(player);
11330
11331       MovePlayer(player, dx, dy);
11332     }
11333
11334     CheckSingleStepMode(player);
11335
11336     SetPlayerWaiting(player, FALSE);
11337
11338     return player_action;
11339   }
11340   else
11341   {
11342     // no actions for this player (no input at player's configured device)
11343
11344     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11345     SnapField(player, 0, 0);
11346     CheckGravityMovementWhenNotMoving(player);
11347
11348     if (player->MovPos == 0)
11349       SetPlayerWaiting(player, TRUE);
11350
11351     if (player->MovPos == 0)    // needed for tape.playing
11352       player->is_moving = FALSE;
11353
11354     player->is_dropping = FALSE;
11355     player->is_dropping_pressed = FALSE;
11356     player->drop_pressed_delay = 0;
11357
11358     CheckSingleStepMode(player);
11359
11360     return 0;
11361   }
11362 }
11363
11364 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11365                                          byte *tape_action)
11366 {
11367   if (!tape.use_mouse_actions)
11368     return;
11369
11370   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11371   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11372   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11373 }
11374
11375 static void SetTapeActionFromMouseAction(byte *tape_action,
11376                                          struct MouseActionInfo *mouse_action)
11377 {
11378   if (!tape.use_mouse_actions)
11379     return;
11380
11381   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11382   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11383   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11384 }
11385
11386 static void CheckLevelSolved(void)
11387 {
11388   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11389   {
11390     if (game_em.level_solved &&
11391         !game_em.game_over)                             // game won
11392     {
11393       LevelSolved();
11394
11395       game_em.game_over = TRUE;
11396
11397       game.all_players_gone = TRUE;
11398     }
11399
11400     if (game_em.game_over)                              // game lost
11401       game.all_players_gone = TRUE;
11402   }
11403   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11404   {
11405     if (game_sp.level_solved &&
11406         !game_sp.game_over)                             // game won
11407     {
11408       LevelSolved();
11409
11410       game_sp.game_over = TRUE;
11411
11412       game.all_players_gone = TRUE;
11413     }
11414
11415     if (game_sp.game_over)                              // game lost
11416       game.all_players_gone = TRUE;
11417   }
11418   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11419   {
11420     if (game_mm.level_solved &&
11421         !game_mm.game_over)                             // game won
11422     {
11423       LevelSolved();
11424
11425       game_mm.game_over = TRUE;
11426
11427       game.all_players_gone = TRUE;
11428     }
11429
11430     if (game_mm.game_over)                              // game lost
11431       game.all_players_gone = TRUE;
11432   }
11433 }
11434
11435 static void CheckLevelTime(void)
11436 {
11437   int i;
11438
11439   if (TimeFrames >= FRAMES_PER_SECOND)
11440   {
11441     TimeFrames = 0;
11442     TapeTime++;
11443
11444     for (i = 0; i < MAX_PLAYERS; i++)
11445     {
11446       struct PlayerInfo *player = &stored_player[i];
11447
11448       if (SHIELD_ON(player))
11449       {
11450         player->shield_normal_time_left--;
11451
11452         if (player->shield_deadly_time_left > 0)
11453           player->shield_deadly_time_left--;
11454       }
11455     }
11456
11457     if (!game.LevelSolved && !level.use_step_counter)
11458     {
11459       TimePlayed++;
11460
11461       if (TimeLeft > 0)
11462       {
11463         TimeLeft--;
11464
11465         if (TimeLeft <= 10 && setup.time_limit)
11466           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11467
11468         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11469            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11470
11471         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11472
11473         if (!TimeLeft && setup.time_limit)
11474         {
11475           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11476             game_em.lev->killed_out_of_time = TRUE;
11477           else
11478             for (i = 0; i < MAX_PLAYERS; i++)
11479               KillPlayer(&stored_player[i]);
11480         }
11481       }
11482       else if (game.no_time_limit && !game.all_players_gone)
11483       {
11484         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11485       }
11486
11487       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11488     }
11489
11490     if (tape.recording || tape.playing)
11491       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11492   }
11493
11494   if (tape.recording || tape.playing)
11495     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11496
11497   UpdateAndDisplayGameControlValues();
11498 }
11499
11500 void AdvanceFrameAndPlayerCounters(int player_nr)
11501 {
11502   int i;
11503
11504   // advance frame counters (global frame counter and time frame counter)
11505   FrameCounter++;
11506   TimeFrames++;
11507
11508   // advance player counters (counters for move delay, move animation etc.)
11509   for (i = 0; i < MAX_PLAYERS; i++)
11510   {
11511     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11512     int move_delay_value = stored_player[i].move_delay_value;
11513     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11514
11515     if (!advance_player_counters)       // not all players may be affected
11516       continue;
11517
11518     if (move_frames == 0)       // less than one move per game frame
11519     {
11520       int stepsize = TILEX / move_delay_value;
11521       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11522       int count = (stored_player[i].is_moving ?
11523                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11524
11525       if (count % delay == 0)
11526         move_frames = 1;
11527     }
11528
11529     stored_player[i].Frame += move_frames;
11530
11531     if (stored_player[i].MovPos != 0)
11532       stored_player[i].StepFrame += move_frames;
11533
11534     if (stored_player[i].move_delay > 0)
11535       stored_player[i].move_delay--;
11536
11537     // due to bugs in previous versions, counter must count up, not down
11538     if (stored_player[i].push_delay != -1)
11539       stored_player[i].push_delay++;
11540
11541     if (stored_player[i].drop_delay > 0)
11542       stored_player[i].drop_delay--;
11543
11544     if (stored_player[i].is_dropping_pressed)
11545       stored_player[i].drop_pressed_delay++;
11546   }
11547 }
11548
11549 void StartGameActions(boolean init_network_game, boolean record_tape,
11550                       int random_seed)
11551 {
11552   unsigned int new_random_seed = InitRND(random_seed);
11553
11554   if (record_tape)
11555     TapeStartRecording(new_random_seed);
11556
11557   if (init_network_game)
11558   {
11559     SendToServer_LevelFile();
11560     SendToServer_StartPlaying();
11561
11562     return;
11563   }
11564
11565   InitGame();
11566 }
11567
11568 static void GameActionsExt(void)
11569 {
11570 #if 0
11571   static unsigned int game_frame_delay = 0;
11572 #endif
11573   unsigned int game_frame_delay_value;
11574   byte *recorded_player_action;
11575   byte summarized_player_action = 0;
11576   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11577   int i;
11578
11579   // detect endless loops, caused by custom element programming
11580   if (recursion_loop_detected && recursion_loop_depth == 0)
11581   {
11582     char *message = getStringCat3("Internal Error! Element ",
11583                                   EL_NAME(recursion_loop_element),
11584                                   " caused endless loop! Quit the game?");
11585
11586     Warn("element '%s' caused endless loop in game engine",
11587          EL_NAME(recursion_loop_element));
11588
11589     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11590
11591     recursion_loop_detected = FALSE;    // if game should be continued
11592
11593     free(message);
11594
11595     return;
11596   }
11597
11598   if (game.restart_level)
11599     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11600
11601   CheckLevelSolved();
11602
11603   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11604     GameWon();
11605
11606   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11607     TapeStop();
11608
11609   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11610     return;
11611
11612   game_frame_delay_value =
11613     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11614
11615   if (tape.playing && tape.warp_forward && !tape.pausing)
11616     game_frame_delay_value = 0;
11617
11618   SetVideoFrameDelay(game_frame_delay_value);
11619
11620   // (de)activate virtual buttons depending on current game status
11621   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11622   {
11623     if (game.all_players_gone)  // if no players there to be controlled anymore
11624       SetOverlayActive(FALSE);
11625     else if (!tape.playing)     // if game continues after tape stopped playing
11626       SetOverlayActive(TRUE);
11627   }
11628
11629 #if 0
11630 #if 0
11631   // ---------- main game synchronization point ----------
11632
11633   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11634
11635   Debug("game:playing:skip", "skip == %d", skip);
11636
11637 #else
11638   // ---------- main game synchronization point ----------
11639
11640   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11641 #endif
11642 #endif
11643
11644   if (network_playing && !network_player_action_received)
11645   {
11646     // try to get network player actions in time
11647
11648     // last chance to get network player actions without main loop delay
11649     HandleNetworking();
11650
11651     // game was quit by network peer
11652     if (game_status != GAME_MODE_PLAYING)
11653       return;
11654
11655     // check if network player actions still missing and game still running
11656     if (!network_player_action_received && !checkGameEnded())
11657       return;           // failed to get network player actions in time
11658
11659     // do not yet reset "network_player_action_received" (for tape.pausing)
11660   }
11661
11662   if (tape.pausing)
11663     return;
11664
11665   // at this point we know that we really continue executing the game
11666
11667   network_player_action_received = FALSE;
11668
11669   // when playing tape, read previously recorded player input from tape data
11670   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11671
11672   local_player->effective_mouse_action = local_player->mouse_action;
11673
11674   if (recorded_player_action != NULL)
11675     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11676                                  recorded_player_action);
11677
11678   // TapePlayAction() may return NULL when toggling to "pause before death"
11679   if (tape.pausing)
11680     return;
11681
11682   if (tape.set_centered_player)
11683   {
11684     game.centered_player_nr_next = tape.centered_player_nr_next;
11685     game.set_centered_player = TRUE;
11686   }
11687
11688   for (i = 0; i < MAX_PLAYERS; i++)
11689   {
11690     summarized_player_action |= stored_player[i].action;
11691
11692     if (!network_playing && (game.team_mode || tape.playing))
11693       stored_player[i].effective_action = stored_player[i].action;
11694   }
11695
11696   if (network_playing && !checkGameEnded())
11697     SendToServer_MovePlayer(summarized_player_action);
11698
11699   // summarize all actions at local players mapped input device position
11700   // (this allows using different input devices in single player mode)
11701   if (!network.enabled && !game.team_mode)
11702     stored_player[map_player_action[local_player->index_nr]].effective_action =
11703       summarized_player_action;
11704
11705   // summarize all actions at centered player in local team mode
11706   if (tape.recording &&
11707       setup.team_mode && !network.enabled &&
11708       setup.input_on_focus &&
11709       game.centered_player_nr != -1)
11710   {
11711     for (i = 0; i < MAX_PLAYERS; i++)
11712       stored_player[map_player_action[i]].effective_action =
11713         (i == game.centered_player_nr ? summarized_player_action : 0);
11714   }
11715
11716   if (recorded_player_action != NULL)
11717     for (i = 0; i < MAX_PLAYERS; i++)
11718       stored_player[i].effective_action = recorded_player_action[i];
11719
11720   for (i = 0; i < MAX_PLAYERS; i++)
11721   {
11722     tape_action[i] = stored_player[i].effective_action;
11723
11724     /* (this may happen in the RND game engine if a player was not present on
11725        the playfield on level start, but appeared later from a custom element */
11726     if (setup.team_mode &&
11727         tape.recording &&
11728         tape_action[i] &&
11729         !tape.player_participates[i])
11730       tape.player_participates[i] = TRUE;
11731   }
11732
11733   SetTapeActionFromMouseAction(tape_action,
11734                                &local_player->effective_mouse_action);
11735
11736   // only record actions from input devices, but not programmed actions
11737   if (tape.recording)
11738     TapeRecordAction(tape_action);
11739
11740   // remember if game was played (especially after tape stopped playing)
11741   if (!tape.playing && summarized_player_action)
11742     game.GamePlayed = TRUE;
11743
11744 #if USE_NEW_PLAYER_ASSIGNMENTS
11745   // !!! also map player actions in single player mode !!!
11746   // if (game.team_mode)
11747   if (1)
11748   {
11749     byte mapped_action[MAX_PLAYERS];
11750
11751 #if DEBUG_PLAYER_ACTIONS
11752     for (i = 0; i < MAX_PLAYERS; i++)
11753       DebugContinued("", "%d, ", stored_player[i].effective_action);
11754 #endif
11755
11756     for (i = 0; i < MAX_PLAYERS; i++)
11757       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11758
11759     for (i = 0; i < MAX_PLAYERS; i++)
11760       stored_player[i].effective_action = mapped_action[i];
11761
11762 #if DEBUG_PLAYER_ACTIONS
11763     DebugContinued("", "=> ");
11764     for (i = 0; i < MAX_PLAYERS; i++)
11765       DebugContinued("", "%d, ", stored_player[i].effective_action);
11766     DebugContinued("game:playing:player", "\n");
11767 #endif
11768   }
11769 #if DEBUG_PLAYER_ACTIONS
11770   else
11771   {
11772     for (i = 0; i < MAX_PLAYERS; i++)
11773       DebugContinued("", "%d, ", stored_player[i].effective_action);
11774     DebugContinued("game:playing:player", "\n");
11775   }
11776 #endif
11777 #endif
11778
11779   for (i = 0; i < MAX_PLAYERS; i++)
11780   {
11781     // allow engine snapshot in case of changed movement attempt
11782     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11783         (stored_player[i].effective_action & KEY_MOTION))
11784       game.snapshot.changed_action = TRUE;
11785
11786     // allow engine snapshot in case of snapping/dropping attempt
11787     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11788         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11789       game.snapshot.changed_action = TRUE;
11790
11791     game.snapshot.last_action[i] = stored_player[i].effective_action;
11792   }
11793
11794   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11795   {
11796     GameActions_EM_Main();
11797   }
11798   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11799   {
11800     GameActions_SP_Main();
11801   }
11802   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11803   {
11804     GameActions_MM_Main();
11805   }
11806   else
11807   {
11808     GameActions_RND_Main();
11809   }
11810
11811   BlitScreenToBitmap(backbuffer);
11812
11813   CheckLevelSolved();
11814   CheckLevelTime();
11815
11816   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11817
11818   if (global.show_frames_per_second)
11819   {
11820     static unsigned int fps_counter = 0;
11821     static int fps_frames = 0;
11822     unsigned int fps_delay_ms = Counter() - fps_counter;
11823
11824     fps_frames++;
11825
11826     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11827     {
11828       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11829
11830       fps_frames = 0;
11831       fps_counter = Counter();
11832
11833       // always draw FPS to screen after FPS value was updated
11834       redraw_mask |= REDRAW_FPS;
11835     }
11836
11837     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11838     if (GetDrawDeactivationMask() == REDRAW_NONE)
11839       redraw_mask |= REDRAW_FPS;
11840   }
11841 }
11842
11843 static void GameActions_CheckSaveEngineSnapshot(void)
11844 {
11845   if (!game.snapshot.save_snapshot)
11846     return;
11847
11848   // clear flag for saving snapshot _before_ saving snapshot
11849   game.snapshot.save_snapshot = FALSE;
11850
11851   SaveEngineSnapshotToList();
11852 }
11853
11854 void GameActions(void)
11855 {
11856   GameActionsExt();
11857
11858   GameActions_CheckSaveEngineSnapshot();
11859 }
11860
11861 void GameActions_EM_Main(void)
11862 {
11863   byte effective_action[MAX_PLAYERS];
11864   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11865   int i;
11866
11867   for (i = 0; i < MAX_PLAYERS; i++)
11868     effective_action[i] = stored_player[i].effective_action;
11869
11870   GameActions_EM(effective_action, warp_mode);
11871 }
11872
11873 void GameActions_SP_Main(void)
11874 {
11875   byte effective_action[MAX_PLAYERS];
11876   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11877   int i;
11878
11879   for (i = 0; i < MAX_PLAYERS; i++)
11880     effective_action[i] = stored_player[i].effective_action;
11881
11882   GameActions_SP(effective_action, warp_mode);
11883
11884   for (i = 0; i < MAX_PLAYERS; i++)
11885   {
11886     if (stored_player[i].force_dropping)
11887       stored_player[i].action |= KEY_BUTTON_DROP;
11888
11889     stored_player[i].force_dropping = FALSE;
11890   }
11891 }
11892
11893 void GameActions_MM_Main(void)
11894 {
11895   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11896
11897   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11898 }
11899
11900 void GameActions_RND_Main(void)
11901 {
11902   GameActions_RND();
11903 }
11904
11905 void GameActions_RND(void)
11906 {
11907   static struct MouseActionInfo mouse_action_last = { 0 };
11908   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
11909   int magic_wall_x = 0, magic_wall_y = 0;
11910   int i, x, y, element, graphic, last_gfx_frame;
11911
11912   InitPlayfieldScanModeVars();
11913
11914   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11915   {
11916     SCAN_PLAYFIELD(x, y)
11917     {
11918       ChangeCount[x][y] = 0;
11919       ChangeEvent[x][y] = -1;
11920     }
11921   }
11922
11923   if (game.set_centered_player)
11924   {
11925     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11926
11927     // switching to "all players" only possible if all players fit to screen
11928     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11929     {
11930       game.centered_player_nr_next = game.centered_player_nr;
11931       game.set_centered_player = FALSE;
11932     }
11933
11934     // do not switch focus to non-existing (or non-active) player
11935     if (game.centered_player_nr_next >= 0 &&
11936         !stored_player[game.centered_player_nr_next].active)
11937     {
11938       game.centered_player_nr_next = game.centered_player_nr;
11939       game.set_centered_player = FALSE;
11940     }
11941   }
11942
11943   if (game.set_centered_player &&
11944       ScreenMovPos == 0)        // screen currently aligned at tile position
11945   {
11946     int sx, sy;
11947
11948     if (game.centered_player_nr_next == -1)
11949     {
11950       setScreenCenteredToAllPlayers(&sx, &sy);
11951     }
11952     else
11953     {
11954       sx = stored_player[game.centered_player_nr_next].jx;
11955       sy = stored_player[game.centered_player_nr_next].jy;
11956     }
11957
11958     game.centered_player_nr = game.centered_player_nr_next;
11959     game.set_centered_player = FALSE;
11960
11961     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11962     DrawGameDoorValues();
11963   }
11964
11965   // check single step mode (set flag and clear again if any player is active)
11966   game.enter_single_step_mode =
11967     (tape.single_step && tape.recording && !tape.pausing);
11968
11969   for (i = 0; i < MAX_PLAYERS; i++)
11970   {
11971     int actual_player_action = stored_player[i].effective_action;
11972
11973 #if 1
11974     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11975        - rnd_equinox_tetrachloride 048
11976        - rnd_equinox_tetrachloride_ii 096
11977        - rnd_emanuel_schmieg 002
11978        - doctor_sloan_ww 001, 020
11979     */
11980     if (stored_player[i].MovPos == 0)
11981       CheckGravityMovement(&stored_player[i]);
11982 #endif
11983
11984     // overwrite programmed action with tape action
11985     if (stored_player[i].programmed_action)
11986       actual_player_action = stored_player[i].programmed_action;
11987
11988     PlayerActions(&stored_player[i], actual_player_action);
11989
11990     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11991   }
11992
11993   // single step pause mode may already have been toggled by "ScrollPlayer()"
11994   if (game.enter_single_step_mode && !tape.pausing)
11995     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11996
11997   ScrollScreen(NULL, SCROLL_GO_ON);
11998
11999   /* for backwards compatibility, the following code emulates a fixed bug that
12000      occured when pushing elements (causing elements that just made their last
12001      pushing step to already (if possible) make their first falling step in the
12002      same game frame, which is bad); this code is also needed to use the famous
12003      "spring push bug" which is used in older levels and might be wanted to be
12004      used also in newer levels, but in this case the buggy pushing code is only
12005      affecting the "spring" element and no other elements */
12006
12007   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12008   {
12009     for (i = 0; i < MAX_PLAYERS; i++)
12010     {
12011       struct PlayerInfo *player = &stored_player[i];
12012       int x = player->jx;
12013       int y = player->jy;
12014
12015       if (player->active && player->is_pushing && player->is_moving &&
12016           IS_MOVING(x, y) &&
12017           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12018            Tile[x][y] == EL_SPRING))
12019       {
12020         ContinueMoving(x, y);
12021
12022         // continue moving after pushing (this is actually a bug)
12023         if (!IS_MOVING(x, y))
12024           Stop[x][y] = FALSE;
12025       }
12026     }
12027   }
12028
12029   SCAN_PLAYFIELD(x, y)
12030   {
12031     Last[x][y] = Tile[x][y];
12032
12033     ChangeCount[x][y] = 0;
12034     ChangeEvent[x][y] = -1;
12035
12036     // this must be handled before main playfield loop
12037     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12038     {
12039       MovDelay[x][y]--;
12040       if (MovDelay[x][y] <= 0)
12041         RemoveField(x, y);
12042     }
12043
12044     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12045     {
12046       MovDelay[x][y]--;
12047       if (MovDelay[x][y] <= 0)
12048       {
12049         int element = Store[x][y];
12050         int move_direction = MovDir[x][y];
12051         int player_index_bit = Store2[x][y];
12052
12053         Store[x][y] = 0;
12054         Store2[x][y] = 0;
12055
12056         RemoveField(x, y);
12057         TEST_DrawLevelField(x, y);
12058
12059         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12060       }
12061     }
12062
12063 #if DEBUG
12064     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12065     {
12066       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12067             x, y);
12068       Debug("game:playing:GameActions_RND", "This should never happen!");
12069
12070       ChangePage[x][y] = -1;
12071     }
12072 #endif
12073
12074     Stop[x][y] = FALSE;
12075     if (WasJustMoving[x][y] > 0)
12076       WasJustMoving[x][y]--;
12077     if (WasJustFalling[x][y] > 0)
12078       WasJustFalling[x][y]--;
12079     if (CheckCollision[x][y] > 0)
12080       CheckCollision[x][y]--;
12081     if (CheckImpact[x][y] > 0)
12082       CheckImpact[x][y]--;
12083
12084     GfxFrame[x][y]++;
12085
12086     /* reset finished pushing action (not done in ContinueMoving() to allow
12087        continuous pushing animation for elements with zero push delay) */
12088     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12089     {
12090       ResetGfxAnimation(x, y);
12091       TEST_DrawLevelField(x, y);
12092     }
12093
12094 #if DEBUG
12095     if (IS_BLOCKED(x, y))
12096     {
12097       int oldx, oldy;
12098
12099       Blocked2Moving(x, y, &oldx, &oldy);
12100       if (!IS_MOVING(oldx, oldy))
12101       {
12102         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12103         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12104         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12105         Debug("game:playing:GameActions_RND", "This should never happen!");
12106       }
12107     }
12108 #endif
12109   }
12110
12111   if (mouse_action.button)
12112   {
12113     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12114
12115     x = mouse_action.lx;
12116     y = mouse_action.ly;
12117     element = Tile[x][y];
12118
12119     if (new_button)
12120     {
12121       CheckElementChange(x, y, element, EL_UNDEFINED, CE_CLICKED_BY_MOUSE);
12122       CheckTriggeredElementChange(x, y, element, CE_MOUSE_CLICKED_ON_X);
12123     }
12124
12125     CheckElementChange(x, y, element, EL_UNDEFINED, CE_PRESSED_BY_MOUSE);
12126     CheckTriggeredElementChange(x, y, element, CE_MOUSE_PRESSED_ON_X);
12127   }
12128
12129   SCAN_PLAYFIELD(x, y)
12130   {
12131     element = Tile[x][y];
12132     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12133     last_gfx_frame = GfxFrame[x][y];
12134
12135     ResetGfxFrame(x, y);
12136
12137     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12138       DrawLevelGraphicAnimation(x, y, graphic);
12139
12140     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12141         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12142       ResetRandomAnimationValue(x, y);
12143
12144     SetRandomAnimationValue(x, y);
12145
12146     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12147
12148     if (IS_INACTIVE(element))
12149     {
12150       if (IS_ANIMATED(graphic))
12151         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12152
12153       continue;
12154     }
12155
12156     // this may take place after moving, so 'element' may have changed
12157     if (IS_CHANGING(x, y) &&
12158         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12159     {
12160       int page = element_info[element].event_page_nr[CE_DELAY];
12161
12162       HandleElementChange(x, y, page);
12163
12164       element = Tile[x][y];
12165       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12166     }
12167
12168     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12169     {
12170       StartMoving(x, y);
12171
12172       element = Tile[x][y];
12173       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12174
12175       if (IS_ANIMATED(graphic) &&
12176           !IS_MOVING(x, y) &&
12177           !Stop[x][y])
12178         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12179
12180       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12181         TEST_DrawTwinkleOnField(x, y);
12182     }
12183     else if (element == EL_ACID)
12184     {
12185       if (!Stop[x][y])
12186         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12187     }
12188     else if ((element == EL_EXIT_OPEN ||
12189               element == EL_EM_EXIT_OPEN ||
12190               element == EL_SP_EXIT_OPEN ||
12191               element == EL_STEEL_EXIT_OPEN ||
12192               element == EL_EM_STEEL_EXIT_OPEN ||
12193               element == EL_SP_TERMINAL ||
12194               element == EL_SP_TERMINAL_ACTIVE ||
12195               element == EL_EXTRA_TIME ||
12196               element == EL_SHIELD_NORMAL ||
12197               element == EL_SHIELD_DEADLY) &&
12198              IS_ANIMATED(graphic))
12199       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12200     else if (IS_MOVING(x, y))
12201       ContinueMoving(x, y);
12202     else if (IS_ACTIVE_BOMB(element))
12203       CheckDynamite(x, y);
12204     else if (element == EL_AMOEBA_GROWING)
12205       AmoebaGrowing(x, y);
12206     else if (element == EL_AMOEBA_SHRINKING)
12207       AmoebaShrinking(x, y);
12208
12209 #if !USE_NEW_AMOEBA_CODE
12210     else if (IS_AMOEBALIVE(element))
12211       AmoebaReproduce(x, y);
12212 #endif
12213
12214     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12215       Life(x, y);
12216     else if (element == EL_EXIT_CLOSED)
12217       CheckExit(x, y);
12218     else if (element == EL_EM_EXIT_CLOSED)
12219       CheckExitEM(x, y);
12220     else if (element == EL_STEEL_EXIT_CLOSED)
12221       CheckExitSteel(x, y);
12222     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12223       CheckExitSteelEM(x, y);
12224     else if (element == EL_SP_EXIT_CLOSED)
12225       CheckExitSP(x, y);
12226     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12227              element == EL_EXPANDABLE_STEELWALL_GROWING)
12228       MauerWaechst(x, y);
12229     else if (element == EL_EXPANDABLE_WALL ||
12230              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12231              element == EL_EXPANDABLE_WALL_VERTICAL ||
12232              element == EL_EXPANDABLE_WALL_ANY ||
12233              element == EL_BD_EXPANDABLE_WALL)
12234       MauerAbleger(x, y);
12235     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12236              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12237              element == EL_EXPANDABLE_STEELWALL_ANY)
12238       MauerAblegerStahl(x, y);
12239     else if (element == EL_FLAMES)
12240       CheckForDragon(x, y);
12241     else if (element == EL_EXPLOSION)
12242       ; // drawing of correct explosion animation is handled separately
12243     else if (element == EL_ELEMENT_SNAPPING ||
12244              element == EL_DIAGONAL_SHRINKING ||
12245              element == EL_DIAGONAL_GROWING)
12246     {
12247       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12248
12249       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12250     }
12251     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12252       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12253
12254     if (IS_BELT_ACTIVE(element))
12255       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12256
12257     if (game.magic_wall_active)
12258     {
12259       int jx = local_player->jx, jy = local_player->jy;
12260
12261       // play the element sound at the position nearest to the player
12262       if ((element == EL_MAGIC_WALL_FULL ||
12263            element == EL_MAGIC_WALL_ACTIVE ||
12264            element == EL_MAGIC_WALL_EMPTYING ||
12265            element == EL_BD_MAGIC_WALL_FULL ||
12266            element == EL_BD_MAGIC_WALL_ACTIVE ||
12267            element == EL_BD_MAGIC_WALL_EMPTYING ||
12268            element == EL_DC_MAGIC_WALL_FULL ||
12269            element == EL_DC_MAGIC_WALL_ACTIVE ||
12270            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12271           ABS(x - jx) + ABS(y - jy) <
12272           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12273       {
12274         magic_wall_x = x;
12275         magic_wall_y = y;
12276       }
12277     }
12278   }
12279
12280 #if USE_NEW_AMOEBA_CODE
12281   // new experimental amoeba growth stuff
12282   if (!(FrameCounter % 8))
12283   {
12284     static unsigned int random = 1684108901;
12285
12286     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12287     {
12288       x = RND(lev_fieldx);
12289       y = RND(lev_fieldy);
12290       element = Tile[x][y];
12291
12292       if (!IS_PLAYER(x,y) &&
12293           (element == EL_EMPTY ||
12294            CAN_GROW_INTO(element) ||
12295            element == EL_QUICKSAND_EMPTY ||
12296            element == EL_QUICKSAND_FAST_EMPTY ||
12297            element == EL_ACID_SPLASH_LEFT ||
12298            element == EL_ACID_SPLASH_RIGHT))
12299       {
12300         if ((IN_LEV_FIELD(x, y-1) && Tile[x][y-1] == EL_AMOEBA_WET) ||
12301             (IN_LEV_FIELD(x-1, y) && Tile[x-1][y] == EL_AMOEBA_WET) ||
12302             (IN_LEV_FIELD(x+1, y) && Tile[x+1][y] == EL_AMOEBA_WET) ||
12303             (IN_LEV_FIELD(x, y+1) && Tile[x][y+1] == EL_AMOEBA_WET))
12304           Tile[x][y] = EL_AMOEBA_DROP;
12305       }
12306
12307       random = random * 129 + 1;
12308     }
12309   }
12310 #endif
12311
12312   game.explosions_delayed = FALSE;
12313
12314   SCAN_PLAYFIELD(x, y)
12315   {
12316     element = Tile[x][y];
12317
12318     if (ExplodeField[x][y])
12319       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12320     else if (element == EL_EXPLOSION)
12321       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12322
12323     ExplodeField[x][y] = EX_TYPE_NONE;
12324   }
12325
12326   game.explosions_delayed = TRUE;
12327
12328   if (game.magic_wall_active)
12329   {
12330     if (!(game.magic_wall_time_left % 4))
12331     {
12332       int element = Tile[magic_wall_x][magic_wall_y];
12333
12334       if (element == EL_BD_MAGIC_WALL_FULL ||
12335           element == EL_BD_MAGIC_WALL_ACTIVE ||
12336           element == EL_BD_MAGIC_WALL_EMPTYING)
12337         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12338       else if (element == EL_DC_MAGIC_WALL_FULL ||
12339                element == EL_DC_MAGIC_WALL_ACTIVE ||
12340                element == EL_DC_MAGIC_WALL_EMPTYING)
12341         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12342       else
12343         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12344     }
12345
12346     if (game.magic_wall_time_left > 0)
12347     {
12348       game.magic_wall_time_left--;
12349
12350       if (!game.magic_wall_time_left)
12351       {
12352         SCAN_PLAYFIELD(x, y)
12353         {
12354           element = Tile[x][y];
12355
12356           if (element == EL_MAGIC_WALL_ACTIVE ||
12357               element == EL_MAGIC_WALL_FULL)
12358           {
12359             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12360             TEST_DrawLevelField(x, y);
12361           }
12362           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12363                    element == EL_BD_MAGIC_WALL_FULL)
12364           {
12365             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12366             TEST_DrawLevelField(x, y);
12367           }
12368           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12369                    element == EL_DC_MAGIC_WALL_FULL)
12370           {
12371             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12372             TEST_DrawLevelField(x, y);
12373           }
12374         }
12375
12376         game.magic_wall_active = FALSE;
12377       }
12378     }
12379   }
12380
12381   if (game.light_time_left > 0)
12382   {
12383     game.light_time_left--;
12384
12385     if (game.light_time_left == 0)
12386       RedrawAllLightSwitchesAndInvisibleElements();
12387   }
12388
12389   if (game.timegate_time_left > 0)
12390   {
12391     game.timegate_time_left--;
12392
12393     if (game.timegate_time_left == 0)
12394       CloseAllOpenTimegates();
12395   }
12396
12397   if (game.lenses_time_left > 0)
12398   {
12399     game.lenses_time_left--;
12400
12401     if (game.lenses_time_left == 0)
12402       RedrawAllInvisibleElementsForLenses();
12403   }
12404
12405   if (game.magnify_time_left > 0)
12406   {
12407     game.magnify_time_left--;
12408
12409     if (game.magnify_time_left == 0)
12410       RedrawAllInvisibleElementsForMagnifier();
12411   }
12412
12413   for (i = 0; i < MAX_PLAYERS; i++)
12414   {
12415     struct PlayerInfo *player = &stored_player[i];
12416
12417     if (SHIELD_ON(player))
12418     {
12419       if (player->shield_deadly_time_left)
12420         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12421       else if (player->shield_normal_time_left)
12422         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12423     }
12424   }
12425
12426 #if USE_DELAYED_GFX_REDRAW
12427   SCAN_PLAYFIELD(x, y)
12428   {
12429     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12430     {
12431       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12432          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12433
12434       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12435         DrawLevelField(x, y);
12436
12437       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12438         DrawLevelFieldCrumbled(x, y);
12439
12440       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12441         DrawLevelFieldCrumbledNeighbours(x, y);
12442
12443       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12444         DrawTwinkleOnField(x, y);
12445     }
12446
12447     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12448   }
12449 #endif
12450
12451   DrawAllPlayers();
12452   PlayAllPlayersSound();
12453
12454   for (i = 0; i < MAX_PLAYERS; i++)
12455   {
12456     struct PlayerInfo *player = &stored_player[i];
12457
12458     if (player->show_envelope != 0 && (!player->active ||
12459                                        player->MovPos == 0))
12460     {
12461       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12462
12463       player->show_envelope = 0;
12464     }
12465   }
12466
12467   // use random number generator in every frame to make it less predictable
12468   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12469     RND(1);
12470
12471   mouse_action_last = mouse_action;
12472 }
12473
12474 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12475 {
12476   int min_x = x, min_y = y, max_x = x, max_y = y;
12477   int scr_fieldx = getScreenFieldSizeX();
12478   int scr_fieldy = getScreenFieldSizeY();
12479   int i;
12480
12481   for (i = 0; i < MAX_PLAYERS; i++)
12482   {
12483     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12484
12485     if (!stored_player[i].active || &stored_player[i] == player)
12486       continue;
12487
12488     min_x = MIN(min_x, jx);
12489     min_y = MIN(min_y, jy);
12490     max_x = MAX(max_x, jx);
12491     max_y = MAX(max_y, jy);
12492   }
12493
12494   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12495 }
12496
12497 static boolean AllPlayersInVisibleScreen(void)
12498 {
12499   int i;
12500
12501   for (i = 0; i < MAX_PLAYERS; i++)
12502   {
12503     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12504
12505     if (!stored_player[i].active)
12506       continue;
12507
12508     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12509       return FALSE;
12510   }
12511
12512   return TRUE;
12513 }
12514
12515 void ScrollLevel(int dx, int dy)
12516 {
12517   int scroll_offset = 2 * TILEX_VAR;
12518   int x, y;
12519
12520   BlitBitmap(drawto_field, drawto_field,
12521              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12522              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12523              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12524              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12525              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12526              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12527
12528   if (dx != 0)
12529   {
12530     x = (dx == 1 ? BX1 : BX2);
12531     for (y = BY1; y <= BY2; y++)
12532       DrawScreenField(x, y);
12533   }
12534
12535   if (dy != 0)
12536   {
12537     y = (dy == 1 ? BY1 : BY2);
12538     for (x = BX1; x <= BX2; x++)
12539       DrawScreenField(x, y);
12540   }
12541
12542   redraw_mask |= REDRAW_FIELD;
12543 }
12544
12545 static boolean canFallDown(struct PlayerInfo *player)
12546 {
12547   int jx = player->jx, jy = player->jy;
12548
12549   return (IN_LEV_FIELD(jx, jy + 1) &&
12550           (IS_FREE(jx, jy + 1) ||
12551            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12552           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12553           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12554 }
12555
12556 static boolean canPassField(int x, int y, int move_dir)
12557 {
12558   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12559   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12560   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12561   int nextx = x + dx;
12562   int nexty = y + dy;
12563   int element = Tile[x][y];
12564
12565   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12566           !CAN_MOVE(element) &&
12567           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12568           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12569           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12570 }
12571
12572 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12573 {
12574   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12575   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12576   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12577   int newx = x + dx;
12578   int newy = y + dy;
12579
12580   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12581           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12582           (IS_DIGGABLE(Tile[newx][newy]) ||
12583            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12584            canPassField(newx, newy, move_dir)));
12585 }
12586
12587 static void CheckGravityMovement(struct PlayerInfo *player)
12588 {
12589   if (player->gravity && !player->programmed_action)
12590   {
12591     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12592     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12593     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12594     int jx = player->jx, jy = player->jy;
12595     boolean player_is_moving_to_valid_field =
12596       (!player_is_snapping &&
12597        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12598         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12599     boolean player_can_fall_down = canFallDown(player);
12600
12601     if (player_can_fall_down &&
12602         !player_is_moving_to_valid_field)
12603       player->programmed_action = MV_DOWN;
12604   }
12605 }
12606
12607 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12608 {
12609   return CheckGravityMovement(player);
12610
12611   if (player->gravity && !player->programmed_action)
12612   {
12613     int jx = player->jx, jy = player->jy;
12614     boolean field_under_player_is_free =
12615       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12616     boolean player_is_standing_on_valid_field =
12617       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12618        (IS_WALKABLE(Tile[jx][jy]) &&
12619         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12620
12621     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12622       player->programmed_action = MV_DOWN;
12623   }
12624 }
12625
12626 /*
12627   MovePlayerOneStep()
12628   -----------------------------------------------------------------------------
12629   dx, dy:               direction (non-diagonal) to try to move the player to
12630   real_dx, real_dy:     direction as read from input device (can be diagonal)
12631 */
12632
12633 boolean MovePlayerOneStep(struct PlayerInfo *player,
12634                           int dx, int dy, int real_dx, int real_dy)
12635 {
12636   int jx = player->jx, jy = player->jy;
12637   int new_jx = jx + dx, new_jy = jy + dy;
12638   int can_move;
12639   boolean player_can_move = !player->cannot_move;
12640
12641   if (!player->active || (!dx && !dy))
12642     return MP_NO_ACTION;
12643
12644   player->MovDir = (dx < 0 ? MV_LEFT :
12645                     dx > 0 ? MV_RIGHT :
12646                     dy < 0 ? MV_UP :
12647                     dy > 0 ? MV_DOWN :  MV_NONE);
12648
12649   if (!IN_LEV_FIELD(new_jx, new_jy))
12650     return MP_NO_ACTION;
12651
12652   if (!player_can_move)
12653   {
12654     if (player->MovPos == 0)
12655     {
12656       player->is_moving = FALSE;
12657       player->is_digging = FALSE;
12658       player->is_collecting = FALSE;
12659       player->is_snapping = FALSE;
12660       player->is_pushing = FALSE;
12661     }
12662   }
12663
12664   if (!network.enabled && game.centered_player_nr == -1 &&
12665       !AllPlayersInSight(player, new_jx, new_jy))
12666     return MP_NO_ACTION;
12667
12668   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12669   if (can_move != MP_MOVING)
12670     return can_move;
12671
12672   // check if DigField() has caused relocation of the player
12673   if (player->jx != jx || player->jy != jy)
12674     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12675
12676   StorePlayer[jx][jy] = 0;
12677   player->last_jx = jx;
12678   player->last_jy = jy;
12679   player->jx = new_jx;
12680   player->jy = new_jy;
12681   StorePlayer[new_jx][new_jy] = player->element_nr;
12682
12683   if (player->move_delay_value_next != -1)
12684   {
12685     player->move_delay_value = player->move_delay_value_next;
12686     player->move_delay_value_next = -1;
12687   }
12688
12689   player->MovPos =
12690     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12691
12692   player->step_counter++;
12693
12694   PlayerVisit[jx][jy] = FrameCounter;
12695
12696   player->is_moving = TRUE;
12697
12698 #if 1
12699   // should better be called in MovePlayer(), but this breaks some tapes
12700   ScrollPlayer(player, SCROLL_INIT);
12701 #endif
12702
12703   return MP_MOVING;
12704 }
12705
12706 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12707 {
12708   int jx = player->jx, jy = player->jy;
12709   int old_jx = jx, old_jy = jy;
12710   int moved = MP_NO_ACTION;
12711
12712   if (!player->active)
12713     return FALSE;
12714
12715   if (!dx && !dy)
12716   {
12717     if (player->MovPos == 0)
12718     {
12719       player->is_moving = FALSE;
12720       player->is_digging = FALSE;
12721       player->is_collecting = FALSE;
12722       player->is_snapping = FALSE;
12723       player->is_pushing = FALSE;
12724     }
12725
12726     return FALSE;
12727   }
12728
12729   if (player->move_delay > 0)
12730     return FALSE;
12731
12732   player->move_delay = -1;              // set to "uninitialized" value
12733
12734   // store if player is automatically moved to next field
12735   player->is_auto_moving = (player->programmed_action != MV_NONE);
12736
12737   // remove the last programmed player action
12738   player->programmed_action = 0;
12739
12740   if (player->MovPos)
12741   {
12742     // should only happen if pre-1.2 tape recordings are played
12743     // this is only for backward compatibility
12744
12745     int original_move_delay_value = player->move_delay_value;
12746
12747 #if DEBUG
12748     Debug("game:playing:MovePlayer",
12749           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12750           tape.counter);
12751 #endif
12752
12753     // scroll remaining steps with finest movement resolution
12754     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12755
12756     while (player->MovPos)
12757     {
12758       ScrollPlayer(player, SCROLL_GO_ON);
12759       ScrollScreen(NULL, SCROLL_GO_ON);
12760
12761       AdvanceFrameAndPlayerCounters(player->index_nr);
12762
12763       DrawAllPlayers();
12764       BackToFront_WithFrameDelay(0);
12765     }
12766
12767     player->move_delay_value = original_move_delay_value;
12768   }
12769
12770   player->is_active = FALSE;
12771
12772   if (player->last_move_dir & MV_HORIZONTAL)
12773   {
12774     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12775       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12776   }
12777   else
12778   {
12779     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12780       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12781   }
12782
12783   if (!moved && !player->is_active)
12784   {
12785     player->is_moving = FALSE;
12786     player->is_digging = FALSE;
12787     player->is_collecting = FALSE;
12788     player->is_snapping = FALSE;
12789     player->is_pushing = FALSE;
12790   }
12791
12792   jx = player->jx;
12793   jy = player->jy;
12794
12795   if (moved & MP_MOVING && !ScreenMovPos &&
12796       (player->index_nr == game.centered_player_nr ||
12797        game.centered_player_nr == -1))
12798   {
12799     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12800
12801     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12802     {
12803       // actual player has left the screen -- scroll in that direction
12804       if (jx != old_jx)         // player has moved horizontally
12805         scroll_x += (jx - old_jx);
12806       else                      // player has moved vertically
12807         scroll_y += (jy - old_jy);
12808     }
12809     else
12810     {
12811       int offset_raw = game.scroll_delay_value;
12812
12813       if (jx != old_jx)         // player has moved horizontally
12814       {
12815         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12816         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12817         int new_scroll_x = jx - MIDPOSX + offset_x;
12818
12819         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12820             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12821           scroll_x = new_scroll_x;
12822
12823         // don't scroll over playfield boundaries
12824         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12825
12826         // don't scroll more than one field at a time
12827         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12828
12829         // don't scroll against the player's moving direction
12830         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12831             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12832           scroll_x = old_scroll_x;
12833       }
12834       else                      // player has moved vertically
12835       {
12836         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12837         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12838         int new_scroll_y = jy - MIDPOSY + offset_y;
12839
12840         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12841             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12842           scroll_y = new_scroll_y;
12843
12844         // don't scroll over playfield boundaries
12845         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
12846
12847         // don't scroll more than one field at a time
12848         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12849
12850         // don't scroll against the player's moving direction
12851         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12852             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12853           scroll_y = old_scroll_y;
12854       }
12855     }
12856
12857     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12858     {
12859       if (!network.enabled && game.centered_player_nr == -1 &&
12860           !AllPlayersInVisibleScreen())
12861       {
12862         scroll_x = old_scroll_x;
12863         scroll_y = old_scroll_y;
12864       }
12865       else
12866       {
12867         ScrollScreen(player, SCROLL_INIT);
12868         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12869       }
12870     }
12871   }
12872
12873   player->StepFrame = 0;
12874
12875   if (moved & MP_MOVING)
12876   {
12877     if (old_jx != jx && old_jy == jy)
12878       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12879     else if (old_jx == jx && old_jy != jy)
12880       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12881
12882     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12883
12884     player->last_move_dir = player->MovDir;
12885     player->is_moving = TRUE;
12886     player->is_snapping = FALSE;
12887     player->is_switching = FALSE;
12888     player->is_dropping = FALSE;
12889     player->is_dropping_pressed = FALSE;
12890     player->drop_pressed_delay = 0;
12891
12892 #if 0
12893     // should better be called here than above, but this breaks some tapes
12894     ScrollPlayer(player, SCROLL_INIT);
12895 #endif
12896   }
12897   else
12898   {
12899     CheckGravityMovementWhenNotMoving(player);
12900
12901     player->is_moving = FALSE;
12902
12903     /* at this point, the player is allowed to move, but cannot move right now
12904        (e.g. because of something blocking the way) -- ensure that the player
12905        is also allowed to move in the next frame (in old versions before 3.1.1,
12906        the player was forced to wait again for eight frames before next try) */
12907
12908     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12909       player->move_delay = 0;   // allow direct movement in the next frame
12910   }
12911
12912   if (player->move_delay == -1)         // not yet initialized by DigField()
12913     player->move_delay = player->move_delay_value;
12914
12915   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12916   {
12917     TestIfPlayerTouchesBadThing(jx, jy);
12918     TestIfPlayerTouchesCustomElement(jx, jy);
12919   }
12920
12921   if (!player->active)
12922     RemovePlayer(player);
12923
12924   return moved;
12925 }
12926
12927 void ScrollPlayer(struct PlayerInfo *player, int mode)
12928 {
12929   int jx = player->jx, jy = player->jy;
12930   int last_jx = player->last_jx, last_jy = player->last_jy;
12931   int move_stepsize = TILEX / player->move_delay_value;
12932
12933   if (!player->active)
12934     return;
12935
12936   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
12937     return;
12938
12939   if (mode == SCROLL_INIT)
12940   {
12941     player->actual_frame_counter = FrameCounter;
12942     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12943
12944     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12945         Tile[last_jx][last_jy] == EL_EMPTY)
12946     {
12947       int last_field_block_delay = 0;   // start with no blocking at all
12948       int block_delay_adjustment = player->block_delay_adjustment;
12949
12950       // if player blocks last field, add delay for exactly one move
12951       if (player->block_last_field)
12952       {
12953         last_field_block_delay += player->move_delay_value;
12954
12955         // when blocking enabled, prevent moving up despite gravity
12956         if (player->gravity && player->MovDir == MV_UP)
12957           block_delay_adjustment = -1;
12958       }
12959
12960       // add block delay adjustment (also possible when not blocking)
12961       last_field_block_delay += block_delay_adjustment;
12962
12963       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12964       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12965     }
12966
12967     if (player->MovPos != 0)    // player has not yet reached destination
12968       return;
12969   }
12970   else if (!FrameReached(&player->actual_frame_counter, 1))
12971     return;
12972
12973   if (player->MovPos != 0)
12974   {
12975     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12976     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12977
12978     // before DrawPlayer() to draw correct player graphic for this case
12979     if (player->MovPos == 0)
12980       CheckGravityMovement(player);
12981   }
12982
12983   if (player->MovPos == 0)      // player reached destination field
12984   {
12985     if (player->move_delay_reset_counter > 0)
12986     {
12987       player->move_delay_reset_counter--;
12988
12989       if (player->move_delay_reset_counter == 0)
12990       {
12991         // continue with normal speed after quickly moving through gate
12992         HALVE_PLAYER_SPEED(player);
12993
12994         // be able to make the next move without delay
12995         player->move_delay = 0;
12996       }
12997     }
12998
12999     player->last_jx = jx;
13000     player->last_jy = jy;
13001
13002     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13003         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13004         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13005         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13006         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13007         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13008         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13009         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13010     {
13011       ExitPlayer(player);
13012
13013       if (game.players_still_needed == 0 &&
13014           (game.friends_still_needed == 0 ||
13015            IS_SP_ELEMENT(Tile[jx][jy])))
13016         LevelSolved();
13017     }
13018
13019     // this breaks one level: "machine", level 000
13020     {
13021       int move_direction = player->MovDir;
13022       int enter_side = MV_DIR_OPPOSITE(move_direction);
13023       int leave_side = move_direction;
13024       int old_jx = last_jx;
13025       int old_jy = last_jy;
13026       int old_element = Tile[old_jx][old_jy];
13027       int new_element = Tile[jx][jy];
13028
13029       if (IS_CUSTOM_ELEMENT(old_element))
13030         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13031                                    CE_LEFT_BY_PLAYER,
13032                                    player->index_bit, leave_side);
13033
13034       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13035                                           CE_PLAYER_LEAVES_X,
13036                                           player->index_bit, leave_side);
13037
13038       if (IS_CUSTOM_ELEMENT(new_element))
13039         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13040                                    player->index_bit, enter_side);
13041
13042       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13043                                           CE_PLAYER_ENTERS_X,
13044                                           player->index_bit, enter_side);
13045
13046       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13047                                         CE_MOVE_OF_X, move_direction);
13048     }
13049
13050     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13051     {
13052       TestIfPlayerTouchesBadThing(jx, jy);
13053       TestIfPlayerTouchesCustomElement(jx, jy);
13054
13055       /* needed because pushed element has not yet reached its destination,
13056          so it would trigger a change event at its previous field location */
13057       if (!player->is_pushing)
13058         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13059
13060       if (level.finish_dig_collect &&
13061           (player->is_digging || player->is_collecting))
13062       {
13063         int last_element = player->last_removed_element;
13064         int move_direction = player->MovDir;
13065         int enter_side = MV_DIR_OPPOSITE(move_direction);
13066         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13067                             CE_PLAYER_COLLECTS_X);
13068
13069         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13070                                             player->index_bit, enter_side);
13071
13072         player->last_removed_element = EL_UNDEFINED;
13073       }
13074
13075       if (!player->active)
13076         RemovePlayer(player);
13077     }
13078
13079     if (!game.LevelSolved && level.use_step_counter)
13080     {
13081       int i;
13082
13083       TimePlayed++;
13084
13085       if (TimeLeft > 0)
13086       {
13087         TimeLeft--;
13088
13089         if (TimeLeft <= 10 && setup.time_limit)
13090           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13091
13092         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13093
13094         DisplayGameControlValues();
13095
13096         if (!TimeLeft && setup.time_limit)
13097           for (i = 0; i < MAX_PLAYERS; i++)
13098             KillPlayer(&stored_player[i]);
13099       }
13100       else if (game.no_time_limit && !game.all_players_gone)
13101       {
13102         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13103
13104         DisplayGameControlValues();
13105       }
13106     }
13107
13108     if (tape.single_step && tape.recording && !tape.pausing &&
13109         !player->programmed_action)
13110       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13111
13112     if (!player->programmed_action)
13113       CheckSaveEngineSnapshot(player);
13114   }
13115 }
13116
13117 void ScrollScreen(struct PlayerInfo *player, int mode)
13118 {
13119   static unsigned int screen_frame_counter = 0;
13120
13121   if (mode == SCROLL_INIT)
13122   {
13123     // set scrolling step size according to actual player's moving speed
13124     ScrollStepSize = TILEX / player->move_delay_value;
13125
13126     screen_frame_counter = FrameCounter;
13127     ScreenMovDir = player->MovDir;
13128     ScreenMovPos = player->MovPos;
13129     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13130     return;
13131   }
13132   else if (!FrameReached(&screen_frame_counter, 1))
13133     return;
13134
13135   if (ScreenMovPos)
13136   {
13137     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13138     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13139     redraw_mask |= REDRAW_FIELD;
13140   }
13141   else
13142     ScreenMovDir = MV_NONE;
13143 }
13144
13145 void TestIfPlayerTouchesCustomElement(int x, int y)
13146 {
13147   static int xy[4][2] =
13148   {
13149     { 0, -1 },
13150     { -1, 0 },
13151     { +1, 0 },
13152     { 0, +1 }
13153   };
13154   static int trigger_sides[4][2] =
13155   {
13156     // center side       border side
13157     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13158     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13159     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13160     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13161   };
13162   static int touch_dir[4] =
13163   {
13164     MV_LEFT | MV_RIGHT,
13165     MV_UP   | MV_DOWN,
13166     MV_UP   | MV_DOWN,
13167     MV_LEFT | MV_RIGHT
13168   };
13169   int center_element = Tile[x][y];      // should always be non-moving!
13170   int i;
13171
13172   for (i = 0; i < NUM_DIRECTIONS; i++)
13173   {
13174     int xx = x + xy[i][0];
13175     int yy = y + xy[i][1];
13176     int center_side = trigger_sides[i][0];
13177     int border_side = trigger_sides[i][1];
13178     int border_element;
13179
13180     if (!IN_LEV_FIELD(xx, yy))
13181       continue;
13182
13183     if (IS_PLAYER(x, y))                // player found at center element
13184     {
13185       struct PlayerInfo *player = PLAYERINFO(x, y);
13186
13187       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13188         border_element = Tile[xx][yy];          // may be moving!
13189       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13190         border_element = Tile[xx][yy];
13191       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13192         border_element = MovingOrBlocked2Element(xx, yy);
13193       else
13194         continue;               // center and border element do not touch
13195
13196       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13197                                  player->index_bit, border_side);
13198       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13199                                           CE_PLAYER_TOUCHES_X,
13200                                           player->index_bit, border_side);
13201
13202       {
13203         /* use player element that is initially defined in the level playfield,
13204            not the player element that corresponds to the runtime player number
13205            (example: a level that contains EL_PLAYER_3 as the only player would
13206            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13207         int player_element = PLAYERINFO(x, y)->initial_element;
13208
13209         CheckElementChangeBySide(xx, yy, border_element, player_element,
13210                                  CE_TOUCHING_X, border_side);
13211       }
13212     }
13213     else if (IS_PLAYER(xx, yy))         // player found at border element
13214     {
13215       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13216
13217       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13218       {
13219         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13220           continue;             // center and border element do not touch
13221       }
13222
13223       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13224                                  player->index_bit, center_side);
13225       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13226                                           CE_PLAYER_TOUCHES_X,
13227                                           player->index_bit, center_side);
13228
13229       {
13230         /* use player element that is initially defined in the level playfield,
13231            not the player element that corresponds to the runtime player number
13232            (example: a level that contains EL_PLAYER_3 as the only player would
13233            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13234         int player_element = PLAYERINFO(xx, yy)->initial_element;
13235
13236         CheckElementChangeBySide(x, y, center_element, player_element,
13237                                  CE_TOUCHING_X, center_side);
13238       }
13239
13240       break;
13241     }
13242   }
13243 }
13244
13245 void TestIfElementTouchesCustomElement(int x, int y)
13246 {
13247   static int xy[4][2] =
13248   {
13249     { 0, -1 },
13250     { -1, 0 },
13251     { +1, 0 },
13252     { 0, +1 }
13253   };
13254   static int trigger_sides[4][2] =
13255   {
13256     // center side      border side
13257     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13258     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13259     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13260     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13261   };
13262   static int touch_dir[4] =
13263   {
13264     MV_LEFT | MV_RIGHT,
13265     MV_UP   | MV_DOWN,
13266     MV_UP   | MV_DOWN,
13267     MV_LEFT | MV_RIGHT
13268   };
13269   boolean change_center_element = FALSE;
13270   int center_element = Tile[x][y];      // should always be non-moving!
13271   int border_element_old[NUM_DIRECTIONS];
13272   int i;
13273
13274   for (i = 0; i < NUM_DIRECTIONS; i++)
13275   {
13276     int xx = x + xy[i][0];
13277     int yy = y + xy[i][1];
13278     int border_element;
13279
13280     border_element_old[i] = -1;
13281
13282     if (!IN_LEV_FIELD(xx, yy))
13283       continue;
13284
13285     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13286       border_element = Tile[xx][yy];    // may be moving!
13287     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13288       border_element = Tile[xx][yy];
13289     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13290       border_element = MovingOrBlocked2Element(xx, yy);
13291     else
13292       continue;                 // center and border element do not touch
13293
13294     border_element_old[i] = border_element;
13295   }
13296
13297   for (i = 0; i < NUM_DIRECTIONS; i++)
13298   {
13299     int xx = x + xy[i][0];
13300     int yy = y + xy[i][1];
13301     int center_side = trigger_sides[i][0];
13302     int border_element = border_element_old[i];
13303
13304     if (border_element == -1)
13305       continue;
13306
13307     // check for change of border element
13308     CheckElementChangeBySide(xx, yy, border_element, center_element,
13309                              CE_TOUCHING_X, center_side);
13310
13311     // (center element cannot be player, so we dont have to check this here)
13312   }
13313
13314   for (i = 0; i < NUM_DIRECTIONS; i++)
13315   {
13316     int xx = x + xy[i][0];
13317     int yy = y + xy[i][1];
13318     int border_side = trigger_sides[i][1];
13319     int border_element = border_element_old[i];
13320
13321     if (border_element == -1)
13322       continue;
13323
13324     // check for change of center element (but change it only once)
13325     if (!change_center_element)
13326       change_center_element =
13327         CheckElementChangeBySide(x, y, center_element, border_element,
13328                                  CE_TOUCHING_X, border_side);
13329
13330     if (IS_PLAYER(xx, yy))
13331     {
13332       /* use player element that is initially defined in the level playfield,
13333          not the player element that corresponds to the runtime player number
13334          (example: a level that contains EL_PLAYER_3 as the only player would
13335          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13336       int player_element = PLAYERINFO(xx, yy)->initial_element;
13337
13338       CheckElementChangeBySide(x, y, center_element, player_element,
13339                                CE_TOUCHING_X, border_side);
13340     }
13341   }
13342 }
13343
13344 void TestIfElementHitsCustomElement(int x, int y, int direction)
13345 {
13346   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13347   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13348   int hitx = x + dx, hity = y + dy;
13349   int hitting_element = Tile[x][y];
13350   int touched_element;
13351
13352   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13353     return;
13354
13355   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13356                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13357
13358   if (IN_LEV_FIELD(hitx, hity))
13359   {
13360     int opposite_direction = MV_DIR_OPPOSITE(direction);
13361     int hitting_side = direction;
13362     int touched_side = opposite_direction;
13363     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13364                           MovDir[hitx][hity] != direction ||
13365                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13366
13367     object_hit = TRUE;
13368
13369     if (object_hit)
13370     {
13371       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13372                                CE_HITTING_X, touched_side);
13373
13374       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13375                                CE_HIT_BY_X, hitting_side);
13376
13377       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13378                                CE_HIT_BY_SOMETHING, opposite_direction);
13379
13380       if (IS_PLAYER(hitx, hity))
13381       {
13382         /* use player element that is initially defined in the level playfield,
13383            not the player element that corresponds to the runtime player number
13384            (example: a level that contains EL_PLAYER_3 as the only player would
13385            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13386         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13387
13388         CheckElementChangeBySide(x, y, hitting_element, player_element,
13389                                  CE_HITTING_X, touched_side);
13390       }
13391     }
13392   }
13393
13394   // "hitting something" is also true when hitting the playfield border
13395   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13396                            CE_HITTING_SOMETHING, direction);
13397 }
13398
13399 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13400 {
13401   int i, kill_x = -1, kill_y = -1;
13402
13403   int bad_element = -1;
13404   static int test_xy[4][2] =
13405   {
13406     { 0, -1 },
13407     { -1, 0 },
13408     { +1, 0 },
13409     { 0, +1 }
13410   };
13411   static int test_dir[4] =
13412   {
13413     MV_UP,
13414     MV_LEFT,
13415     MV_RIGHT,
13416     MV_DOWN
13417   };
13418
13419   for (i = 0; i < NUM_DIRECTIONS; i++)
13420   {
13421     int test_x, test_y, test_move_dir, test_element;
13422
13423     test_x = good_x + test_xy[i][0];
13424     test_y = good_y + test_xy[i][1];
13425
13426     if (!IN_LEV_FIELD(test_x, test_y))
13427       continue;
13428
13429     test_move_dir =
13430       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13431
13432     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13433
13434     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13435        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13436     */
13437     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13438         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13439     {
13440       kill_x = test_x;
13441       kill_y = test_y;
13442       bad_element = test_element;
13443
13444       break;
13445     }
13446   }
13447
13448   if (kill_x != -1 || kill_y != -1)
13449   {
13450     if (IS_PLAYER(good_x, good_y))
13451     {
13452       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13453
13454       if (player->shield_deadly_time_left > 0 &&
13455           !IS_INDESTRUCTIBLE(bad_element))
13456         Bang(kill_x, kill_y);
13457       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13458         KillPlayer(player);
13459     }
13460     else
13461       Bang(good_x, good_y);
13462   }
13463 }
13464
13465 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13466 {
13467   int i, kill_x = -1, kill_y = -1;
13468   int bad_element = Tile[bad_x][bad_y];
13469   static int test_xy[4][2] =
13470   {
13471     { 0, -1 },
13472     { -1, 0 },
13473     { +1, 0 },
13474     { 0, +1 }
13475   };
13476   static int touch_dir[4] =
13477   {
13478     MV_LEFT | MV_RIGHT,
13479     MV_UP   | MV_DOWN,
13480     MV_UP   | MV_DOWN,
13481     MV_LEFT | MV_RIGHT
13482   };
13483   static int test_dir[4] =
13484   {
13485     MV_UP,
13486     MV_LEFT,
13487     MV_RIGHT,
13488     MV_DOWN
13489   };
13490
13491   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13492     return;
13493
13494   for (i = 0; i < NUM_DIRECTIONS; i++)
13495   {
13496     int test_x, test_y, test_move_dir, test_element;
13497
13498     test_x = bad_x + test_xy[i][0];
13499     test_y = bad_y + test_xy[i][1];
13500
13501     if (!IN_LEV_FIELD(test_x, test_y))
13502       continue;
13503
13504     test_move_dir =
13505       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13506
13507     test_element = Tile[test_x][test_y];
13508
13509     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13510        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13511     */
13512     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13513         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13514     {
13515       // good thing is player or penguin that does not move away
13516       if (IS_PLAYER(test_x, test_y))
13517       {
13518         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13519
13520         if (bad_element == EL_ROBOT && player->is_moving)
13521           continue;     // robot does not kill player if he is moving
13522
13523         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13524         {
13525           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13526             continue;           // center and border element do not touch
13527         }
13528
13529         kill_x = test_x;
13530         kill_y = test_y;
13531
13532         break;
13533       }
13534       else if (test_element == EL_PENGUIN)
13535       {
13536         kill_x = test_x;
13537         kill_y = test_y;
13538
13539         break;
13540       }
13541     }
13542   }
13543
13544   if (kill_x != -1 || kill_y != -1)
13545   {
13546     if (IS_PLAYER(kill_x, kill_y))
13547     {
13548       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13549
13550       if (player->shield_deadly_time_left > 0 &&
13551           !IS_INDESTRUCTIBLE(bad_element))
13552         Bang(bad_x, bad_y);
13553       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13554         KillPlayer(player);
13555     }
13556     else
13557       Bang(kill_x, kill_y);
13558   }
13559 }
13560
13561 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13562 {
13563   int bad_element = Tile[bad_x][bad_y];
13564   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13565   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13566   int test_x = bad_x + dx, test_y = bad_y + dy;
13567   int test_move_dir, test_element;
13568   int kill_x = -1, kill_y = -1;
13569
13570   if (!IN_LEV_FIELD(test_x, test_y))
13571     return;
13572
13573   test_move_dir =
13574     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13575
13576   test_element = Tile[test_x][test_y];
13577
13578   if (test_move_dir != bad_move_dir)
13579   {
13580     // good thing can be player or penguin that does not move away
13581     if (IS_PLAYER(test_x, test_y))
13582     {
13583       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13584
13585       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13586          player as being hit when he is moving towards the bad thing, because
13587          the "get hit by" condition would be lost after the player stops) */
13588       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13589         return;         // player moves away from bad thing
13590
13591       kill_x = test_x;
13592       kill_y = test_y;
13593     }
13594     else if (test_element == EL_PENGUIN)
13595     {
13596       kill_x = test_x;
13597       kill_y = test_y;
13598     }
13599   }
13600
13601   if (kill_x != -1 || kill_y != -1)
13602   {
13603     if (IS_PLAYER(kill_x, kill_y))
13604     {
13605       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13606
13607       if (player->shield_deadly_time_left > 0 &&
13608           !IS_INDESTRUCTIBLE(bad_element))
13609         Bang(bad_x, bad_y);
13610       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13611         KillPlayer(player);
13612     }
13613     else
13614       Bang(kill_x, kill_y);
13615   }
13616 }
13617
13618 void TestIfPlayerTouchesBadThing(int x, int y)
13619 {
13620   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13621 }
13622
13623 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13624 {
13625   TestIfGoodThingHitsBadThing(x, y, move_dir);
13626 }
13627
13628 void TestIfBadThingTouchesPlayer(int x, int y)
13629 {
13630   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13631 }
13632
13633 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13634 {
13635   TestIfBadThingHitsGoodThing(x, y, move_dir);
13636 }
13637
13638 void TestIfFriendTouchesBadThing(int x, int y)
13639 {
13640   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13641 }
13642
13643 void TestIfBadThingTouchesFriend(int x, int y)
13644 {
13645   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13646 }
13647
13648 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13649 {
13650   int i, kill_x = bad_x, kill_y = bad_y;
13651   static int xy[4][2] =
13652   {
13653     { 0, -1 },
13654     { -1, 0 },
13655     { +1, 0 },
13656     { 0, +1 }
13657   };
13658
13659   for (i = 0; i < NUM_DIRECTIONS; i++)
13660   {
13661     int x, y, element;
13662
13663     x = bad_x + xy[i][0];
13664     y = bad_y + xy[i][1];
13665     if (!IN_LEV_FIELD(x, y))
13666       continue;
13667
13668     element = Tile[x][y];
13669     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13670         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13671     {
13672       kill_x = x;
13673       kill_y = y;
13674       break;
13675     }
13676   }
13677
13678   if (kill_x != bad_x || kill_y != bad_y)
13679     Bang(bad_x, bad_y);
13680 }
13681
13682 void KillPlayer(struct PlayerInfo *player)
13683 {
13684   int jx = player->jx, jy = player->jy;
13685
13686   if (!player->active)
13687     return;
13688
13689 #if 0
13690   Debug("game:playing:KillPlayer",
13691         "0: killed == %d, active == %d, reanimated == %d",
13692         player->killed, player->active, player->reanimated);
13693 #endif
13694
13695   /* the following code was introduced to prevent an infinite loop when calling
13696      -> Bang()
13697      -> CheckTriggeredElementChangeExt()
13698      -> ExecuteCustomElementAction()
13699      -> KillPlayer()
13700      -> (infinitely repeating the above sequence of function calls)
13701      which occurs when killing the player while having a CE with the setting
13702      "kill player X when explosion of <player X>"; the solution using a new
13703      field "player->killed" was chosen for backwards compatibility, although
13704      clever use of the fields "player->active" etc. would probably also work */
13705 #if 1
13706   if (player->killed)
13707     return;
13708 #endif
13709
13710   player->killed = TRUE;
13711
13712   // remove accessible field at the player's position
13713   Tile[jx][jy] = EL_EMPTY;
13714
13715   // deactivate shield (else Bang()/Explode() would not work right)
13716   player->shield_normal_time_left = 0;
13717   player->shield_deadly_time_left = 0;
13718
13719 #if 0
13720   Debug("game:playing:KillPlayer",
13721         "1: killed == %d, active == %d, reanimated == %d",
13722         player->killed, player->active, player->reanimated);
13723 #endif
13724
13725   Bang(jx, jy);
13726
13727 #if 0
13728   Debug("game:playing:KillPlayer",
13729         "2: killed == %d, active == %d, reanimated == %d",
13730         player->killed, player->active, player->reanimated);
13731 #endif
13732
13733   if (player->reanimated)       // killed player may have been reanimated
13734     player->killed = player->reanimated = FALSE;
13735   else
13736     BuryPlayer(player);
13737 }
13738
13739 static void KillPlayerUnlessEnemyProtected(int x, int y)
13740 {
13741   if (!PLAYER_ENEMY_PROTECTED(x, y))
13742     KillPlayer(PLAYERINFO(x, y));
13743 }
13744
13745 static void KillPlayerUnlessExplosionProtected(int x, int y)
13746 {
13747   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13748     KillPlayer(PLAYERINFO(x, y));
13749 }
13750
13751 void BuryPlayer(struct PlayerInfo *player)
13752 {
13753   int jx = player->jx, jy = player->jy;
13754
13755   if (!player->active)
13756     return;
13757
13758   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13759   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13760
13761   RemovePlayer(player);
13762
13763   player->buried = TRUE;
13764
13765   if (game.all_players_gone)
13766     game.GameOver = TRUE;
13767 }
13768
13769 void RemovePlayer(struct PlayerInfo *player)
13770 {
13771   int jx = player->jx, jy = player->jy;
13772   int i, found = FALSE;
13773
13774   player->present = FALSE;
13775   player->active = FALSE;
13776
13777   // required for some CE actions (even if the player is not active anymore)
13778   player->MovPos = 0;
13779
13780   if (!ExplodeField[jx][jy])
13781     StorePlayer[jx][jy] = 0;
13782
13783   if (player->is_moving)
13784     TEST_DrawLevelField(player->last_jx, player->last_jy);
13785
13786   for (i = 0; i < MAX_PLAYERS; i++)
13787     if (stored_player[i].active)
13788       found = TRUE;
13789
13790   if (!found)
13791   {
13792     game.all_players_gone = TRUE;
13793     game.GameOver = TRUE;
13794   }
13795
13796   game.exit_x = game.robot_wheel_x = jx;
13797   game.exit_y = game.robot_wheel_y = jy;
13798 }
13799
13800 void ExitPlayer(struct PlayerInfo *player)
13801 {
13802   DrawPlayer(player);   // needed here only to cleanup last field
13803   RemovePlayer(player);
13804
13805   if (game.players_still_needed > 0)
13806     game.players_still_needed--;
13807 }
13808
13809 static void SetFieldForSnapping(int x, int y, int element, int direction,
13810                                 int player_index_bit)
13811 {
13812   struct ElementInfo *ei = &element_info[element];
13813   int direction_bit = MV_DIR_TO_BIT(direction);
13814   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13815   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13816                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13817
13818   Tile[x][y] = EL_ELEMENT_SNAPPING;
13819   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13820   MovDir[x][y] = direction;
13821   Store[x][y] = element;
13822   Store2[x][y] = player_index_bit;
13823
13824   ResetGfxAnimation(x, y);
13825
13826   GfxElement[x][y] = element;
13827   GfxAction[x][y] = action;
13828   GfxDir[x][y] = direction;
13829   GfxFrame[x][y] = -1;
13830 }
13831
13832 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
13833                                    int player_index_bit)
13834 {
13835   TestIfElementTouchesCustomElement(x, y);      // for empty space
13836
13837   if (level.finish_dig_collect)
13838   {
13839     int dig_side = MV_DIR_OPPOSITE(direction);
13840
13841     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13842                                         player_index_bit, dig_side);
13843   }
13844 }
13845
13846 /*
13847   =============================================================================
13848   checkDiagonalPushing()
13849   -----------------------------------------------------------------------------
13850   check if diagonal input device direction results in pushing of object
13851   (by checking if the alternative direction is walkable, diggable, ...)
13852   =============================================================================
13853 */
13854
13855 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13856                                     int x, int y, int real_dx, int real_dy)
13857 {
13858   int jx, jy, dx, dy, xx, yy;
13859
13860   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13861     return TRUE;
13862
13863   // diagonal direction: check alternative direction
13864   jx = player->jx;
13865   jy = player->jy;
13866   dx = x - jx;
13867   dy = y - jy;
13868   xx = jx + (dx == 0 ? real_dx : 0);
13869   yy = jy + (dy == 0 ? real_dy : 0);
13870
13871   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
13872 }
13873
13874 /*
13875   =============================================================================
13876   DigField()
13877   -----------------------------------------------------------------------------
13878   x, y:                 field next to player (non-diagonal) to try to dig to
13879   real_dx, real_dy:     direction as read from input device (can be diagonal)
13880   =============================================================================
13881 */
13882
13883 static int DigField(struct PlayerInfo *player,
13884                     int oldx, int oldy, int x, int y,
13885                     int real_dx, int real_dy, int mode)
13886 {
13887   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13888   boolean player_was_pushing = player->is_pushing;
13889   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13890   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13891   int jx = oldx, jy = oldy;
13892   int dx = x - jx, dy = y - jy;
13893   int nextx = x + dx, nexty = y + dy;
13894   int move_direction = (dx == -1 ? MV_LEFT  :
13895                         dx == +1 ? MV_RIGHT :
13896                         dy == -1 ? MV_UP    :
13897                         dy == +1 ? MV_DOWN  : MV_NONE);
13898   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13899   int dig_side = MV_DIR_OPPOSITE(move_direction);
13900   int old_element = Tile[jx][jy];
13901   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13902   int collect_count;
13903
13904   if (is_player)                // function can also be called by EL_PENGUIN
13905   {
13906     if (player->MovPos == 0)
13907     {
13908       player->is_digging = FALSE;
13909       player->is_collecting = FALSE;
13910     }
13911
13912     if (player->MovPos == 0)    // last pushing move finished
13913       player->is_pushing = FALSE;
13914
13915     if (mode == DF_NO_PUSH)     // player just stopped pushing
13916     {
13917       player->is_switching = FALSE;
13918       player->push_delay = -1;
13919
13920       return MP_NO_ACTION;
13921     }
13922   }
13923
13924   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13925     old_element = Back[jx][jy];
13926
13927   // in case of element dropped at player position, check background
13928   else if (Back[jx][jy] != EL_EMPTY &&
13929            game.engine_version >= VERSION_IDENT(2,2,0,0))
13930     old_element = Back[jx][jy];
13931
13932   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13933     return MP_NO_ACTION;        // field has no opening in this direction
13934
13935   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13936     return MP_NO_ACTION;        // field has no opening in this direction
13937
13938   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13939   {
13940     SplashAcid(x, y);
13941
13942     Tile[jx][jy] = player->artwork_element;
13943     InitMovingField(jx, jy, MV_DOWN);
13944     Store[jx][jy] = EL_ACID;
13945     ContinueMoving(jx, jy);
13946     BuryPlayer(player);
13947
13948     return MP_DONT_RUN_INTO;
13949   }
13950
13951   if (player_can_move && DONT_RUN_INTO(element))
13952   {
13953     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13954
13955     return MP_DONT_RUN_INTO;
13956   }
13957
13958   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13959     return MP_NO_ACTION;
13960
13961   collect_count = element_info[element].collect_count_initial;
13962
13963   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
13964     return MP_NO_ACTION;
13965
13966   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13967     player_can_move = player_can_move_or_snap;
13968
13969   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13970       game.engine_version >= VERSION_IDENT(2,2,0,0))
13971   {
13972     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13973                                player->index_bit, dig_side);
13974     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13975                                         player->index_bit, dig_side);
13976
13977     if (element == EL_DC_LANDMINE)
13978       Bang(x, y);
13979
13980     if (Tile[x][y] != element)          // field changed by snapping
13981       return MP_ACTION;
13982
13983     return MP_NO_ACTION;
13984   }
13985
13986   if (player->gravity && is_player && !player->is_auto_moving &&
13987       canFallDown(player) && move_direction != MV_DOWN &&
13988       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13989     return MP_NO_ACTION;        // player cannot walk here due to gravity
13990
13991   if (player_can_move &&
13992       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13993   {
13994     int sound_element = SND_ELEMENT(element);
13995     int sound_action = ACTION_WALKING;
13996
13997     if (IS_RND_GATE(element))
13998     {
13999       if (!player->key[RND_GATE_NR(element)])
14000         return MP_NO_ACTION;
14001     }
14002     else if (IS_RND_GATE_GRAY(element))
14003     {
14004       if (!player->key[RND_GATE_GRAY_NR(element)])
14005         return MP_NO_ACTION;
14006     }
14007     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14008     {
14009       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14010         return MP_NO_ACTION;
14011     }
14012     else if (element == EL_EXIT_OPEN ||
14013              element == EL_EM_EXIT_OPEN ||
14014              element == EL_EM_EXIT_OPENING ||
14015              element == EL_STEEL_EXIT_OPEN ||
14016              element == EL_EM_STEEL_EXIT_OPEN ||
14017              element == EL_EM_STEEL_EXIT_OPENING ||
14018              element == EL_SP_EXIT_OPEN ||
14019              element == EL_SP_EXIT_OPENING)
14020     {
14021       sound_action = ACTION_PASSING;    // player is passing exit
14022     }
14023     else if (element == EL_EMPTY)
14024     {
14025       sound_action = ACTION_MOVING;             // nothing to walk on
14026     }
14027
14028     // play sound from background or player, whatever is available
14029     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14030       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14031     else
14032       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14033   }
14034   else if (player_can_move &&
14035            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14036   {
14037     if (!ACCESS_FROM(element, opposite_direction))
14038       return MP_NO_ACTION;      // field not accessible from this direction
14039
14040     if (CAN_MOVE(element))      // only fixed elements can be passed!
14041       return MP_NO_ACTION;
14042
14043     if (IS_EM_GATE(element))
14044     {
14045       if (!player->key[EM_GATE_NR(element)])
14046         return MP_NO_ACTION;
14047     }
14048     else if (IS_EM_GATE_GRAY(element))
14049     {
14050       if (!player->key[EM_GATE_GRAY_NR(element)])
14051         return MP_NO_ACTION;
14052     }
14053     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14054     {
14055       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14056         return MP_NO_ACTION;
14057     }
14058     else if (IS_EMC_GATE(element))
14059     {
14060       if (!player->key[EMC_GATE_NR(element)])
14061         return MP_NO_ACTION;
14062     }
14063     else if (IS_EMC_GATE_GRAY(element))
14064     {
14065       if (!player->key[EMC_GATE_GRAY_NR(element)])
14066         return MP_NO_ACTION;
14067     }
14068     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14069     {
14070       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14071         return MP_NO_ACTION;
14072     }
14073     else if (element == EL_DC_GATE_WHITE ||
14074              element == EL_DC_GATE_WHITE_GRAY ||
14075              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14076     {
14077       if (player->num_white_keys == 0)
14078         return MP_NO_ACTION;
14079
14080       player->num_white_keys--;
14081     }
14082     else if (IS_SP_PORT(element))
14083     {
14084       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14085           element == EL_SP_GRAVITY_PORT_RIGHT ||
14086           element == EL_SP_GRAVITY_PORT_UP ||
14087           element == EL_SP_GRAVITY_PORT_DOWN)
14088         player->gravity = !player->gravity;
14089       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14090                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14091                element == EL_SP_GRAVITY_ON_PORT_UP ||
14092                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14093         player->gravity = TRUE;
14094       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14095                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14096                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14097                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14098         player->gravity = FALSE;
14099     }
14100
14101     // automatically move to the next field with double speed
14102     player->programmed_action = move_direction;
14103
14104     if (player->move_delay_reset_counter == 0)
14105     {
14106       player->move_delay_reset_counter = 2;     // two double speed steps
14107
14108       DOUBLE_PLAYER_SPEED(player);
14109     }
14110
14111     PlayLevelSoundAction(x, y, ACTION_PASSING);
14112   }
14113   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14114   {
14115     RemoveField(x, y);
14116
14117     if (mode != DF_SNAP)
14118     {
14119       GfxElement[x][y] = GFX_ELEMENT(element);
14120       player->is_digging = TRUE;
14121     }
14122
14123     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14124
14125     // use old behaviour for old levels (digging)
14126     if (!level.finish_dig_collect)
14127     {
14128       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14129                                           player->index_bit, dig_side);
14130
14131       // if digging triggered player relocation, finish digging tile
14132       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14133         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14134     }
14135
14136     if (mode == DF_SNAP)
14137     {
14138       if (level.block_snap_field)
14139         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14140       else
14141         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14142
14143       // use old behaviour for old levels (snapping)
14144       if (!level.finish_dig_collect)
14145         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14146                                             player->index_bit, dig_side);
14147     }
14148   }
14149   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14150   {
14151     RemoveField(x, y);
14152
14153     if (is_player && mode != DF_SNAP)
14154     {
14155       GfxElement[x][y] = element;
14156       player->is_collecting = TRUE;
14157     }
14158
14159     if (element == EL_SPEED_PILL)
14160     {
14161       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14162     }
14163     else if (element == EL_EXTRA_TIME && level.time > 0)
14164     {
14165       TimeLeft += level.extra_time;
14166
14167       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14168
14169       DisplayGameControlValues();
14170     }
14171     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14172     {
14173       player->shield_normal_time_left += level.shield_normal_time;
14174       if (element == EL_SHIELD_DEADLY)
14175         player->shield_deadly_time_left += level.shield_deadly_time;
14176     }
14177     else if (element == EL_DYNAMITE ||
14178              element == EL_EM_DYNAMITE ||
14179              element == EL_SP_DISK_RED)
14180     {
14181       if (player->inventory_size < MAX_INVENTORY_SIZE)
14182         player->inventory_element[player->inventory_size++] = element;
14183
14184       DrawGameDoorValues();
14185     }
14186     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14187     {
14188       player->dynabomb_count++;
14189       player->dynabombs_left++;
14190     }
14191     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14192     {
14193       player->dynabomb_size++;
14194     }
14195     else if (element == EL_DYNABOMB_INCREASE_POWER)
14196     {
14197       player->dynabomb_xl = TRUE;
14198     }
14199     else if (IS_KEY(element))
14200     {
14201       player->key[KEY_NR(element)] = TRUE;
14202
14203       DrawGameDoorValues();
14204     }
14205     else if (element == EL_DC_KEY_WHITE)
14206     {
14207       player->num_white_keys++;
14208
14209       // display white keys?
14210       // DrawGameDoorValues();
14211     }
14212     else if (IS_ENVELOPE(element))
14213     {
14214       player->show_envelope = element;
14215     }
14216     else if (element == EL_EMC_LENSES)
14217     {
14218       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14219
14220       RedrawAllInvisibleElementsForLenses();
14221     }
14222     else if (element == EL_EMC_MAGNIFIER)
14223     {
14224       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14225
14226       RedrawAllInvisibleElementsForMagnifier();
14227     }
14228     else if (IS_DROPPABLE(element) ||
14229              IS_THROWABLE(element))     // can be collected and dropped
14230     {
14231       int i;
14232
14233       if (collect_count == 0)
14234         player->inventory_infinite_element = element;
14235       else
14236         for (i = 0; i < collect_count; i++)
14237           if (player->inventory_size < MAX_INVENTORY_SIZE)
14238             player->inventory_element[player->inventory_size++] = element;
14239
14240       DrawGameDoorValues();
14241     }
14242     else if (collect_count > 0)
14243     {
14244       game.gems_still_needed -= collect_count;
14245       if (game.gems_still_needed < 0)
14246         game.gems_still_needed = 0;
14247
14248       game.snapshot.collected_item = TRUE;
14249
14250       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14251
14252       DisplayGameControlValues();
14253     }
14254
14255     RaiseScoreElement(element);
14256     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14257
14258     // use old behaviour for old levels (collecting)
14259     if (!level.finish_dig_collect && is_player)
14260     {
14261       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14262                                           player->index_bit, dig_side);
14263
14264       // if collecting triggered player relocation, finish collecting tile
14265       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14266         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14267     }
14268
14269     if (mode == DF_SNAP)
14270     {
14271       if (level.block_snap_field)
14272         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14273       else
14274         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14275
14276       // use old behaviour for old levels (snapping)
14277       if (!level.finish_dig_collect)
14278         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14279                                             player->index_bit, dig_side);
14280     }
14281   }
14282   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14283   {
14284     if (mode == DF_SNAP && element != EL_BD_ROCK)
14285       return MP_NO_ACTION;
14286
14287     if (CAN_FALL(element) && dy)
14288       return MP_NO_ACTION;
14289
14290     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14291         !(element == EL_SPRING && level.use_spring_bug))
14292       return MP_NO_ACTION;
14293
14294     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14295         ((move_direction & MV_VERTICAL &&
14296           ((element_info[element].move_pattern & MV_LEFT &&
14297             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14298            (element_info[element].move_pattern & MV_RIGHT &&
14299             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14300          (move_direction & MV_HORIZONTAL &&
14301           ((element_info[element].move_pattern & MV_UP &&
14302             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14303            (element_info[element].move_pattern & MV_DOWN &&
14304             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14305       return MP_NO_ACTION;
14306
14307     // do not push elements already moving away faster than player
14308     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14309         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14310       return MP_NO_ACTION;
14311
14312     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14313     {
14314       if (player->push_delay_value == -1 || !player_was_pushing)
14315         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14316     }
14317     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14318     {
14319       if (player->push_delay_value == -1)
14320         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14321     }
14322     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14323     {
14324       if (!player->is_pushing)
14325         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14326     }
14327
14328     player->is_pushing = TRUE;
14329     player->is_active = TRUE;
14330
14331     if (!(IN_LEV_FIELD(nextx, nexty) &&
14332           (IS_FREE(nextx, nexty) ||
14333            (IS_SB_ELEMENT(element) &&
14334             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14335            (IS_CUSTOM_ELEMENT(element) &&
14336             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14337       return MP_NO_ACTION;
14338
14339     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14340       return MP_NO_ACTION;
14341
14342     if (player->push_delay == -1)       // new pushing; restart delay
14343       player->push_delay = 0;
14344
14345     if (player->push_delay < player->push_delay_value &&
14346         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14347         element != EL_SPRING && element != EL_BALLOON)
14348     {
14349       // make sure that there is no move delay before next try to push
14350       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14351         player->move_delay = 0;
14352
14353       return MP_NO_ACTION;
14354     }
14355
14356     if (IS_CUSTOM_ELEMENT(element) &&
14357         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14358     {
14359       if (!DigFieldByCE(nextx, nexty, element))
14360         return MP_NO_ACTION;
14361     }
14362
14363     if (IS_SB_ELEMENT(element))
14364     {
14365       boolean sokoban_task_solved = FALSE;
14366
14367       if (element == EL_SOKOBAN_FIELD_FULL)
14368       {
14369         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14370
14371         IncrementSokobanFieldsNeeded();
14372         IncrementSokobanObjectsNeeded();
14373       }
14374
14375       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14376       {
14377         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14378
14379         DecrementSokobanFieldsNeeded();
14380         DecrementSokobanObjectsNeeded();
14381
14382         // sokoban object was pushed from empty field to sokoban field
14383         if (Back[x][y] == EL_EMPTY)
14384           sokoban_task_solved = TRUE;
14385       }
14386
14387       Tile[x][y] = EL_SOKOBAN_OBJECT;
14388
14389       if (Back[x][y] == Back[nextx][nexty])
14390         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14391       else if (Back[x][y] != 0)
14392         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14393                                     ACTION_EMPTYING);
14394       else
14395         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14396                                     ACTION_FILLING);
14397
14398       if (sokoban_task_solved &&
14399           game.sokoban_fields_still_needed == 0 &&
14400           game.sokoban_objects_still_needed == 0 &&
14401           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14402       {
14403         game.players_still_needed = 0;
14404
14405         LevelSolved();
14406
14407         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14408       }
14409     }
14410     else
14411       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14412
14413     InitMovingField(x, y, move_direction);
14414     GfxAction[x][y] = ACTION_PUSHING;
14415
14416     if (mode == DF_SNAP)
14417       ContinueMoving(x, y);
14418     else
14419       MovPos[x][y] = (dx != 0 ? dx : dy);
14420
14421     Pushed[x][y] = TRUE;
14422     Pushed[nextx][nexty] = TRUE;
14423
14424     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14425       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14426     else
14427       player->push_delay_value = -1;    // get new value later
14428
14429     // check for element change _after_ element has been pushed
14430     if (game.use_change_when_pushing_bug)
14431     {
14432       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14433                                  player->index_bit, dig_side);
14434       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14435                                           player->index_bit, dig_side);
14436     }
14437   }
14438   else if (IS_SWITCHABLE(element))
14439   {
14440     if (PLAYER_SWITCHING(player, x, y))
14441     {
14442       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14443                                           player->index_bit, dig_side);
14444
14445       return MP_ACTION;
14446     }
14447
14448     player->is_switching = TRUE;
14449     player->switch_x = x;
14450     player->switch_y = y;
14451
14452     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14453
14454     if (element == EL_ROBOT_WHEEL)
14455     {
14456       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14457
14458       game.robot_wheel_x = x;
14459       game.robot_wheel_y = y;
14460       game.robot_wheel_active = TRUE;
14461
14462       TEST_DrawLevelField(x, y);
14463     }
14464     else if (element == EL_SP_TERMINAL)
14465     {
14466       int xx, yy;
14467
14468       SCAN_PLAYFIELD(xx, yy)
14469       {
14470         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14471         {
14472           Bang(xx, yy);
14473         }
14474         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14475         {
14476           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14477
14478           ResetGfxAnimation(xx, yy);
14479           TEST_DrawLevelField(xx, yy);
14480         }
14481       }
14482     }
14483     else if (IS_BELT_SWITCH(element))
14484     {
14485       ToggleBeltSwitch(x, y);
14486     }
14487     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14488              element == EL_SWITCHGATE_SWITCH_DOWN ||
14489              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14490              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14491     {
14492       ToggleSwitchgateSwitch(x, y);
14493     }
14494     else if (element == EL_LIGHT_SWITCH ||
14495              element == EL_LIGHT_SWITCH_ACTIVE)
14496     {
14497       ToggleLightSwitch(x, y);
14498     }
14499     else if (element == EL_TIMEGATE_SWITCH ||
14500              element == EL_DC_TIMEGATE_SWITCH)
14501     {
14502       ActivateTimegateSwitch(x, y);
14503     }
14504     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14505              element == EL_BALLOON_SWITCH_RIGHT ||
14506              element == EL_BALLOON_SWITCH_UP    ||
14507              element == EL_BALLOON_SWITCH_DOWN  ||
14508              element == EL_BALLOON_SWITCH_NONE  ||
14509              element == EL_BALLOON_SWITCH_ANY)
14510     {
14511       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14512                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14513                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14514                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14515                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14516                              move_direction);
14517     }
14518     else if (element == EL_LAMP)
14519     {
14520       Tile[x][y] = EL_LAMP_ACTIVE;
14521       game.lights_still_needed--;
14522
14523       ResetGfxAnimation(x, y);
14524       TEST_DrawLevelField(x, y);
14525     }
14526     else if (element == EL_TIME_ORB_FULL)
14527     {
14528       Tile[x][y] = EL_TIME_ORB_EMPTY;
14529
14530       if (level.time > 0 || level.use_time_orb_bug)
14531       {
14532         TimeLeft += level.time_orb_time;
14533         game.no_time_limit = FALSE;
14534
14535         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14536
14537         DisplayGameControlValues();
14538       }
14539
14540       ResetGfxAnimation(x, y);
14541       TEST_DrawLevelField(x, y);
14542     }
14543     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14544              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14545     {
14546       int xx, yy;
14547
14548       game.ball_active = !game.ball_active;
14549
14550       SCAN_PLAYFIELD(xx, yy)
14551       {
14552         int e = Tile[xx][yy];
14553
14554         if (game.ball_active)
14555         {
14556           if (e == EL_EMC_MAGIC_BALL)
14557             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14558           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14559             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14560         }
14561         else
14562         {
14563           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14564             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14565           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14566             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14567         }
14568       }
14569     }
14570
14571     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14572                                         player->index_bit, dig_side);
14573
14574     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14575                                         player->index_bit, dig_side);
14576
14577     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14578                                         player->index_bit, dig_side);
14579
14580     return MP_ACTION;
14581   }
14582   else
14583   {
14584     if (!PLAYER_SWITCHING(player, x, y))
14585     {
14586       player->is_switching = TRUE;
14587       player->switch_x = x;
14588       player->switch_y = y;
14589
14590       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14591                                  player->index_bit, dig_side);
14592       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14593                                           player->index_bit, dig_side);
14594
14595       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14596                                  player->index_bit, dig_side);
14597       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14598                                           player->index_bit, dig_side);
14599     }
14600
14601     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14602                                player->index_bit, dig_side);
14603     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14604                                         player->index_bit, dig_side);
14605
14606     return MP_NO_ACTION;
14607   }
14608
14609   player->push_delay = -1;
14610
14611   if (is_player)                // function can also be called by EL_PENGUIN
14612   {
14613     if (Tile[x][y] != element)          // really digged/collected something
14614     {
14615       player->is_collecting = !player->is_digging;
14616       player->is_active = TRUE;
14617
14618       player->last_removed_element = element;
14619     }
14620   }
14621
14622   return MP_MOVING;
14623 }
14624
14625 static boolean DigFieldByCE(int x, int y, int digging_element)
14626 {
14627   int element = Tile[x][y];
14628
14629   if (!IS_FREE(x, y))
14630   {
14631     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14632                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14633                   ACTION_BREAKING);
14634
14635     // no element can dig solid indestructible elements
14636     if (IS_INDESTRUCTIBLE(element) &&
14637         !IS_DIGGABLE(element) &&
14638         !IS_COLLECTIBLE(element))
14639       return FALSE;
14640
14641     if (AmoebaNr[x][y] &&
14642         (element == EL_AMOEBA_FULL ||
14643          element == EL_BD_AMOEBA ||
14644          element == EL_AMOEBA_GROWING))
14645     {
14646       AmoebaCnt[AmoebaNr[x][y]]--;
14647       AmoebaCnt2[AmoebaNr[x][y]]--;
14648     }
14649
14650     if (IS_MOVING(x, y))
14651       RemoveMovingField(x, y);
14652     else
14653     {
14654       RemoveField(x, y);
14655       TEST_DrawLevelField(x, y);
14656     }
14657
14658     // if digged element was about to explode, prevent the explosion
14659     ExplodeField[x][y] = EX_TYPE_NONE;
14660
14661     PlayLevelSoundAction(x, y, action);
14662   }
14663
14664   Store[x][y] = EL_EMPTY;
14665
14666   // this makes it possible to leave the removed element again
14667   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14668     Store[x][y] = element;
14669
14670   return TRUE;
14671 }
14672
14673 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14674 {
14675   int jx = player->jx, jy = player->jy;
14676   int x = jx + dx, y = jy + dy;
14677   int snap_direction = (dx == -1 ? MV_LEFT  :
14678                         dx == +1 ? MV_RIGHT :
14679                         dy == -1 ? MV_UP    :
14680                         dy == +1 ? MV_DOWN  : MV_NONE);
14681   boolean can_continue_snapping = (level.continuous_snapping &&
14682                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14683
14684   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14685     return FALSE;
14686
14687   if (!player->active || !IN_LEV_FIELD(x, y))
14688     return FALSE;
14689
14690   if (dx && dy)
14691     return FALSE;
14692
14693   if (!dx && !dy)
14694   {
14695     if (player->MovPos == 0)
14696       player->is_pushing = FALSE;
14697
14698     player->is_snapping = FALSE;
14699
14700     if (player->MovPos == 0)
14701     {
14702       player->is_moving = FALSE;
14703       player->is_digging = FALSE;
14704       player->is_collecting = FALSE;
14705     }
14706
14707     return FALSE;
14708   }
14709
14710   // prevent snapping with already pressed snap key when not allowed
14711   if (player->is_snapping && !can_continue_snapping)
14712     return FALSE;
14713
14714   player->MovDir = snap_direction;
14715
14716   if (player->MovPos == 0)
14717   {
14718     player->is_moving = FALSE;
14719     player->is_digging = FALSE;
14720     player->is_collecting = FALSE;
14721   }
14722
14723   player->is_dropping = FALSE;
14724   player->is_dropping_pressed = FALSE;
14725   player->drop_pressed_delay = 0;
14726
14727   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14728     return FALSE;
14729
14730   player->is_snapping = TRUE;
14731   player->is_active = TRUE;
14732
14733   if (player->MovPos == 0)
14734   {
14735     player->is_moving = FALSE;
14736     player->is_digging = FALSE;
14737     player->is_collecting = FALSE;
14738   }
14739
14740   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14741     TEST_DrawLevelField(player->last_jx, player->last_jy);
14742
14743   TEST_DrawLevelField(x, y);
14744
14745   return TRUE;
14746 }
14747
14748 static boolean DropElement(struct PlayerInfo *player)
14749 {
14750   int old_element, new_element;
14751   int dropx = player->jx, dropy = player->jy;
14752   int drop_direction = player->MovDir;
14753   int drop_side = drop_direction;
14754   int drop_element = get_next_dropped_element(player);
14755
14756   /* do not drop an element on top of another element; when holding drop key
14757      pressed without moving, dropped element must move away before the next
14758      element can be dropped (this is especially important if the next element
14759      is dynamite, which can be placed on background for historical reasons) */
14760   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
14761     return MP_ACTION;
14762
14763   if (IS_THROWABLE(drop_element))
14764   {
14765     dropx += GET_DX_FROM_DIR(drop_direction);
14766     dropy += GET_DY_FROM_DIR(drop_direction);
14767
14768     if (!IN_LEV_FIELD(dropx, dropy))
14769       return FALSE;
14770   }
14771
14772   old_element = Tile[dropx][dropy];     // old element at dropping position
14773   new_element = drop_element;           // default: no change when dropping
14774
14775   // check if player is active, not moving and ready to drop
14776   if (!player->active || player->MovPos || player->drop_delay > 0)
14777     return FALSE;
14778
14779   // check if player has anything that can be dropped
14780   if (new_element == EL_UNDEFINED)
14781     return FALSE;
14782
14783   // only set if player has anything that can be dropped
14784   player->is_dropping_pressed = TRUE;
14785
14786   // check if drop key was pressed long enough for EM style dynamite
14787   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14788     return FALSE;
14789
14790   // check if anything can be dropped at the current position
14791   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14792     return FALSE;
14793
14794   // collected custom elements can only be dropped on empty fields
14795   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14796     return FALSE;
14797
14798   if (old_element != EL_EMPTY)
14799     Back[dropx][dropy] = old_element;   // store old element on this field
14800
14801   ResetGfxAnimation(dropx, dropy);
14802   ResetRandomAnimationValue(dropx, dropy);
14803
14804   if (player->inventory_size > 0 ||
14805       player->inventory_infinite_element != EL_UNDEFINED)
14806   {
14807     if (player->inventory_size > 0)
14808     {
14809       player->inventory_size--;
14810
14811       DrawGameDoorValues();
14812
14813       if (new_element == EL_DYNAMITE)
14814         new_element = EL_DYNAMITE_ACTIVE;
14815       else if (new_element == EL_EM_DYNAMITE)
14816         new_element = EL_EM_DYNAMITE_ACTIVE;
14817       else if (new_element == EL_SP_DISK_RED)
14818         new_element = EL_SP_DISK_RED_ACTIVE;
14819     }
14820
14821     Tile[dropx][dropy] = new_element;
14822
14823     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14824       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14825                           el2img(Tile[dropx][dropy]), 0);
14826
14827     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14828
14829     // needed if previous element just changed to "empty" in the last frame
14830     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14831
14832     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14833                                player->index_bit, drop_side);
14834     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14835                                         CE_PLAYER_DROPS_X,
14836                                         player->index_bit, drop_side);
14837
14838     TestIfElementTouchesCustomElement(dropx, dropy);
14839   }
14840   else          // player is dropping a dyna bomb
14841   {
14842     player->dynabombs_left--;
14843
14844     Tile[dropx][dropy] = new_element;
14845
14846     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14847       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14848                           el2img(Tile[dropx][dropy]), 0);
14849
14850     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14851   }
14852
14853   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
14854     InitField_WithBug1(dropx, dropy, FALSE);
14855
14856   new_element = Tile[dropx][dropy];     // element might have changed
14857
14858   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14859       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14860   {
14861     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14862       MovDir[dropx][dropy] = drop_direction;
14863
14864     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14865
14866     // do not cause impact style collision by dropping elements that can fall
14867     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14868   }
14869
14870   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14871   player->is_dropping = TRUE;
14872
14873   player->drop_pressed_delay = 0;
14874   player->is_dropping_pressed = FALSE;
14875
14876   player->drop_x = dropx;
14877   player->drop_y = dropy;
14878
14879   return TRUE;
14880 }
14881
14882 // ----------------------------------------------------------------------------
14883 // game sound playing functions
14884 // ----------------------------------------------------------------------------
14885
14886 static int *loop_sound_frame = NULL;
14887 static int *loop_sound_volume = NULL;
14888
14889 void InitPlayLevelSound(void)
14890 {
14891   int num_sounds = getSoundListSize();
14892
14893   checked_free(loop_sound_frame);
14894   checked_free(loop_sound_volume);
14895
14896   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14897   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14898 }
14899
14900 static void PlayLevelSound(int x, int y, int nr)
14901 {
14902   int sx = SCREENX(x), sy = SCREENY(y);
14903   int volume, stereo_position;
14904   int max_distance = 8;
14905   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14906
14907   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14908       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14909     return;
14910
14911   if (!IN_LEV_FIELD(x, y) ||
14912       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14913       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14914     return;
14915
14916   volume = SOUND_MAX_VOLUME;
14917
14918   if (!IN_SCR_FIELD(sx, sy))
14919   {
14920     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14921     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14922
14923     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14924   }
14925
14926   stereo_position = (SOUND_MAX_LEFT +
14927                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14928                      (SCR_FIELDX + 2 * max_distance));
14929
14930   if (IS_LOOP_SOUND(nr))
14931   {
14932     /* This assures that quieter loop sounds do not overwrite louder ones,
14933        while restarting sound volume comparison with each new game frame. */
14934
14935     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14936       return;
14937
14938     loop_sound_volume[nr] = volume;
14939     loop_sound_frame[nr] = FrameCounter;
14940   }
14941
14942   PlaySoundExt(nr, volume, stereo_position, type);
14943 }
14944
14945 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14946 {
14947   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14948                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14949                  y < LEVELY(BY1) ? LEVELY(BY1) :
14950                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14951                  sound_action);
14952 }
14953
14954 static void PlayLevelSoundAction(int x, int y, int action)
14955 {
14956   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
14957 }
14958
14959 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14960 {
14961   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14962
14963   if (sound_effect != SND_UNDEFINED)
14964     PlayLevelSound(x, y, sound_effect);
14965 }
14966
14967 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14968                                               int action)
14969 {
14970   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14971
14972   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14973     PlayLevelSound(x, y, sound_effect);
14974 }
14975
14976 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14977 {
14978   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
14979
14980   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14981     PlayLevelSound(x, y, sound_effect);
14982 }
14983
14984 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14985 {
14986   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
14987
14988   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14989     StopSound(sound_effect);
14990 }
14991
14992 static int getLevelMusicNr(void)
14993 {
14994   if (levelset.music[level_nr] != MUS_UNDEFINED)
14995     return levelset.music[level_nr];            // from config file
14996   else
14997     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
14998 }
14999
15000 static void FadeLevelSounds(void)
15001 {
15002   FadeSounds();
15003 }
15004
15005 static void FadeLevelMusic(void)
15006 {
15007   int music_nr = getLevelMusicNr();
15008   char *curr_music = getCurrentlyPlayingMusicFilename();
15009   char *next_music = getMusicInfoEntryFilename(music_nr);
15010
15011   if (!strEqual(curr_music, next_music))
15012     FadeMusic();
15013 }
15014
15015 void FadeLevelSoundsAndMusic(void)
15016 {
15017   FadeLevelSounds();
15018   FadeLevelMusic();
15019 }
15020
15021 static void PlayLevelMusic(void)
15022 {
15023   int music_nr = getLevelMusicNr();
15024   char *curr_music = getCurrentlyPlayingMusicFilename();
15025   char *next_music = getMusicInfoEntryFilename(music_nr);
15026
15027   if (!strEqual(curr_music, next_music))
15028     PlayMusicLoop(music_nr);
15029 }
15030
15031 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15032 {
15033   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15034   int offset = 0;
15035   int x = xx - offset;
15036   int y = yy - offset;
15037
15038   switch (sample)
15039   {
15040     case SOUND_blank:
15041       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15042       break;
15043
15044     case SOUND_roll:
15045       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15046       break;
15047
15048     case SOUND_stone:
15049       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15050       break;
15051
15052     case SOUND_nut:
15053       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15054       break;
15055
15056     case SOUND_crack:
15057       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15058       break;
15059
15060     case SOUND_bug:
15061       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15062       break;
15063
15064     case SOUND_tank:
15065       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15066       break;
15067
15068     case SOUND_android_clone:
15069       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15070       break;
15071
15072     case SOUND_android_move:
15073       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15074       break;
15075
15076     case SOUND_spring:
15077       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15078       break;
15079
15080     case SOUND_slurp:
15081       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15082       break;
15083
15084     case SOUND_eater:
15085       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15086       break;
15087
15088     case SOUND_eater_eat:
15089       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15090       break;
15091
15092     case SOUND_alien:
15093       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15094       break;
15095
15096     case SOUND_collect:
15097       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15098       break;
15099
15100     case SOUND_diamond:
15101       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15102       break;
15103
15104     case SOUND_squash:
15105       // !!! CHECK THIS !!!
15106 #if 1
15107       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15108 #else
15109       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15110 #endif
15111       break;
15112
15113     case SOUND_wonderfall:
15114       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15115       break;
15116
15117     case SOUND_drip:
15118       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15119       break;
15120
15121     case SOUND_push:
15122       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15123       break;
15124
15125     case SOUND_dirt:
15126       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15127       break;
15128
15129     case SOUND_acid:
15130       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15131       break;
15132
15133     case SOUND_ball:
15134       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15135       break;
15136
15137     case SOUND_slide:
15138       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15139       break;
15140
15141     case SOUND_wonder:
15142       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15143       break;
15144
15145     case SOUND_door:
15146       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15147       break;
15148
15149     case SOUND_exit_open:
15150       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15151       break;
15152
15153     case SOUND_exit_leave:
15154       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15155       break;
15156
15157     case SOUND_dynamite:
15158       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15159       break;
15160
15161     case SOUND_tick:
15162       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15163       break;
15164
15165     case SOUND_press:
15166       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15167       break;
15168
15169     case SOUND_wheel:
15170       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15171       break;
15172
15173     case SOUND_boom:
15174       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15175       break;
15176
15177     case SOUND_die:
15178       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15179       break;
15180
15181     case SOUND_time:
15182       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15183       break;
15184
15185     default:
15186       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15187       break;
15188   }
15189 }
15190
15191 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15192 {
15193   int element = map_element_SP_to_RND(element_sp);
15194   int action = map_action_SP_to_RND(action_sp);
15195   int offset = (setup.sp_show_border_elements ? 0 : 1);
15196   int x = xx - offset;
15197   int y = yy - offset;
15198
15199   PlayLevelSoundElementAction(x, y, element, action);
15200 }
15201
15202 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15203 {
15204   int element = map_element_MM_to_RND(element_mm);
15205   int action = map_action_MM_to_RND(action_mm);
15206   int offset = 0;
15207   int x = xx - offset;
15208   int y = yy - offset;
15209
15210   if (!IS_MM_ELEMENT(element))
15211     element = EL_MM_DEFAULT;
15212
15213   PlayLevelSoundElementAction(x, y, element, action);
15214 }
15215
15216 void PlaySound_MM(int sound_mm)
15217 {
15218   int sound = map_sound_MM_to_RND(sound_mm);
15219
15220   if (sound == SND_UNDEFINED)
15221     return;
15222
15223   PlaySound(sound);
15224 }
15225
15226 void PlaySoundLoop_MM(int sound_mm)
15227 {
15228   int sound = map_sound_MM_to_RND(sound_mm);
15229
15230   if (sound == SND_UNDEFINED)
15231     return;
15232
15233   PlaySoundLoop(sound);
15234 }
15235
15236 void StopSound_MM(int sound_mm)
15237 {
15238   int sound = map_sound_MM_to_RND(sound_mm);
15239
15240   if (sound == SND_UNDEFINED)
15241     return;
15242
15243   StopSound(sound);
15244 }
15245
15246 void RaiseScore(int value)
15247 {
15248   game.score += value;
15249
15250   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15251
15252   DisplayGameControlValues();
15253 }
15254
15255 void RaiseScoreElement(int element)
15256 {
15257   switch (element)
15258   {
15259     case EL_EMERALD:
15260     case EL_BD_DIAMOND:
15261     case EL_EMERALD_YELLOW:
15262     case EL_EMERALD_RED:
15263     case EL_EMERALD_PURPLE:
15264     case EL_SP_INFOTRON:
15265       RaiseScore(level.score[SC_EMERALD]);
15266       break;
15267     case EL_DIAMOND:
15268       RaiseScore(level.score[SC_DIAMOND]);
15269       break;
15270     case EL_CRYSTAL:
15271       RaiseScore(level.score[SC_CRYSTAL]);
15272       break;
15273     case EL_PEARL:
15274       RaiseScore(level.score[SC_PEARL]);
15275       break;
15276     case EL_BUG:
15277     case EL_BD_BUTTERFLY:
15278     case EL_SP_ELECTRON:
15279       RaiseScore(level.score[SC_BUG]);
15280       break;
15281     case EL_SPACESHIP:
15282     case EL_BD_FIREFLY:
15283     case EL_SP_SNIKSNAK:
15284       RaiseScore(level.score[SC_SPACESHIP]);
15285       break;
15286     case EL_YAMYAM:
15287     case EL_DARK_YAMYAM:
15288       RaiseScore(level.score[SC_YAMYAM]);
15289       break;
15290     case EL_ROBOT:
15291       RaiseScore(level.score[SC_ROBOT]);
15292       break;
15293     case EL_PACMAN:
15294       RaiseScore(level.score[SC_PACMAN]);
15295       break;
15296     case EL_NUT:
15297       RaiseScore(level.score[SC_NUT]);
15298       break;
15299     case EL_DYNAMITE:
15300     case EL_EM_DYNAMITE:
15301     case EL_SP_DISK_RED:
15302     case EL_DYNABOMB_INCREASE_NUMBER:
15303     case EL_DYNABOMB_INCREASE_SIZE:
15304     case EL_DYNABOMB_INCREASE_POWER:
15305       RaiseScore(level.score[SC_DYNAMITE]);
15306       break;
15307     case EL_SHIELD_NORMAL:
15308     case EL_SHIELD_DEADLY:
15309       RaiseScore(level.score[SC_SHIELD]);
15310       break;
15311     case EL_EXTRA_TIME:
15312       RaiseScore(level.extra_time_score);
15313       break;
15314     case EL_KEY_1:
15315     case EL_KEY_2:
15316     case EL_KEY_3:
15317     case EL_KEY_4:
15318     case EL_EM_KEY_1:
15319     case EL_EM_KEY_2:
15320     case EL_EM_KEY_3:
15321     case EL_EM_KEY_4:
15322     case EL_EMC_KEY_5:
15323     case EL_EMC_KEY_6:
15324     case EL_EMC_KEY_7:
15325     case EL_EMC_KEY_8:
15326     case EL_DC_KEY_WHITE:
15327       RaiseScore(level.score[SC_KEY]);
15328       break;
15329     default:
15330       RaiseScore(element_info[element].collect_score);
15331       break;
15332   }
15333 }
15334
15335 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15336 {
15337   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15338   {
15339     // closing door required in case of envelope style request dialogs
15340     if (!skip_request)
15341     {
15342       // prevent short reactivation of overlay buttons while closing door
15343       SetOverlayActive(FALSE);
15344
15345       CloseDoor(DOOR_CLOSE_1);
15346     }
15347
15348     if (network.enabled)
15349       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15350     else
15351     {
15352       if (quick_quit)
15353         FadeSkipNextFadeIn();
15354
15355       SetGameStatus(GAME_MODE_MAIN);
15356
15357       DrawMainMenu();
15358     }
15359   }
15360   else          // continue playing the game
15361   {
15362     if (tape.playing && tape.deactivate_display)
15363       TapeDeactivateDisplayOff(TRUE);
15364
15365     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15366
15367     if (tape.playing && tape.deactivate_display)
15368       TapeDeactivateDisplayOn();
15369   }
15370 }
15371
15372 void RequestQuitGame(boolean ask_if_really_quit)
15373 {
15374   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15375   boolean skip_request = game.all_players_gone || quick_quit;
15376
15377   RequestQuitGameExt(skip_request, quick_quit,
15378                      "Do you really want to quit the game?");
15379 }
15380
15381 void RequestRestartGame(char *message)
15382 {
15383   game.restart_game_message = NULL;
15384
15385   boolean has_started_game = hasStartedNetworkGame();
15386   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15387
15388   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15389   {
15390     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15391   }
15392   else
15393   {
15394     // needed in case of envelope request to close game panel
15395     CloseDoor(DOOR_CLOSE_1);
15396
15397     SetGameStatus(GAME_MODE_MAIN);
15398
15399     DrawMainMenu();
15400   }
15401 }
15402
15403 void CheckGameOver(void)
15404 {
15405   static boolean last_game_over = FALSE;
15406   static int game_over_delay = 0;
15407   int game_over_delay_value = 50;
15408   boolean game_over = checkGameFailed();
15409
15410   // do not handle game over if request dialog is already active
15411   if (game.request_active)
15412     return;
15413
15414   // do not ask to play again if game was never actually played
15415   if (!game.GamePlayed)
15416     return;
15417
15418   if (!game_over)
15419   {
15420     last_game_over = FALSE;
15421     game_over_delay = game_over_delay_value;
15422
15423     return;
15424   }
15425
15426   if (game_over_delay > 0)
15427   {
15428     game_over_delay--;
15429
15430     return;
15431   }
15432
15433   if (last_game_over != game_over)
15434     game.restart_game_message = (hasStartedNetworkGame() ?
15435                                  "Game over! Play it again?" :
15436                                  "Game over!");
15437
15438   last_game_over = game_over;
15439 }
15440
15441 boolean checkGameSolved(void)
15442 {
15443   // set for all game engines if level was solved
15444   return game.LevelSolved_GameEnd;
15445 }
15446
15447 boolean checkGameFailed(void)
15448 {
15449   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15450     return (game_em.game_over && !game_em.level_solved);
15451   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15452     return (game_sp.game_over && !game_sp.level_solved);
15453   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15454     return (game_mm.game_over && !game_mm.level_solved);
15455   else                          // GAME_ENGINE_TYPE_RND
15456     return (game.GameOver && !game.LevelSolved);
15457 }
15458
15459 boolean checkGameEnded(void)
15460 {
15461   return (checkGameSolved() || checkGameFailed());
15462 }
15463
15464
15465 // ----------------------------------------------------------------------------
15466 // random generator functions
15467 // ----------------------------------------------------------------------------
15468
15469 unsigned int InitEngineRandom_RND(int seed)
15470 {
15471   game.num_random_calls = 0;
15472
15473   return InitEngineRandom(seed);
15474 }
15475
15476 unsigned int RND(int max)
15477 {
15478   if (max > 0)
15479   {
15480     game.num_random_calls++;
15481
15482     return GetEngineRandom(max);
15483   }
15484
15485   return 0;
15486 }
15487
15488
15489 // ----------------------------------------------------------------------------
15490 // game engine snapshot handling functions
15491 // ----------------------------------------------------------------------------
15492
15493 struct EngineSnapshotInfo
15494 {
15495   // runtime values for custom element collect score
15496   int collect_score[NUM_CUSTOM_ELEMENTS];
15497
15498   // runtime values for group element choice position
15499   int choice_pos[NUM_GROUP_ELEMENTS];
15500
15501   // runtime values for belt position animations
15502   int belt_graphic[4][NUM_BELT_PARTS];
15503   int belt_anim_mode[4][NUM_BELT_PARTS];
15504 };
15505
15506 static struct EngineSnapshotInfo engine_snapshot_rnd;
15507 static char *snapshot_level_identifier = NULL;
15508 static int snapshot_level_nr = -1;
15509
15510 static void SaveEngineSnapshotValues_RND(void)
15511 {
15512   static int belt_base_active_element[4] =
15513   {
15514     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15515     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15516     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15517     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15518   };
15519   int i, j;
15520
15521   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15522   {
15523     int element = EL_CUSTOM_START + i;
15524
15525     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15526   }
15527
15528   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15529   {
15530     int element = EL_GROUP_START + i;
15531
15532     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15533   }
15534
15535   for (i = 0; i < 4; i++)
15536   {
15537     for (j = 0; j < NUM_BELT_PARTS; j++)
15538     {
15539       int element = belt_base_active_element[i] + j;
15540       int graphic = el2img(element);
15541       int anim_mode = graphic_info[graphic].anim_mode;
15542
15543       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15544       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15545     }
15546   }
15547 }
15548
15549 static void LoadEngineSnapshotValues_RND(void)
15550 {
15551   unsigned int num_random_calls = game.num_random_calls;
15552   int i, j;
15553
15554   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15555   {
15556     int element = EL_CUSTOM_START + i;
15557
15558     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15559   }
15560
15561   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15562   {
15563     int element = EL_GROUP_START + i;
15564
15565     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15566   }
15567
15568   for (i = 0; i < 4; i++)
15569   {
15570     for (j = 0; j < NUM_BELT_PARTS; j++)
15571     {
15572       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15573       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15574
15575       graphic_info[graphic].anim_mode = anim_mode;
15576     }
15577   }
15578
15579   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15580   {
15581     InitRND(tape.random_seed);
15582     for (i = 0; i < num_random_calls; i++)
15583       RND(1);
15584   }
15585
15586   if (game.num_random_calls != num_random_calls)
15587   {
15588     Error("number of random calls out of sync");
15589     Error("number of random calls should be %d", num_random_calls);
15590     Error("number of random calls is %d", game.num_random_calls);
15591
15592     Fail("this should not happen -- please debug");
15593   }
15594 }
15595
15596 void FreeEngineSnapshotSingle(void)
15597 {
15598   FreeSnapshotSingle();
15599
15600   setString(&snapshot_level_identifier, NULL);
15601   snapshot_level_nr = -1;
15602 }
15603
15604 void FreeEngineSnapshotList(void)
15605 {
15606   FreeSnapshotList();
15607 }
15608
15609 static ListNode *SaveEngineSnapshotBuffers(void)
15610 {
15611   ListNode *buffers = NULL;
15612
15613   // copy some special values to a structure better suited for the snapshot
15614
15615   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15616     SaveEngineSnapshotValues_RND();
15617   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15618     SaveEngineSnapshotValues_EM();
15619   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15620     SaveEngineSnapshotValues_SP(&buffers);
15621   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15622     SaveEngineSnapshotValues_MM(&buffers);
15623
15624   // save values stored in special snapshot structure
15625
15626   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15627     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15628   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15629     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15630   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15631     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15632   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15633     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15634
15635   // save further RND engine values
15636
15637   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15638   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15639   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15640
15641   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15642   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15643   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15644   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15645   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15646
15647   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15648   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15649   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15650
15651   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15652
15653   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15654   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15655
15656   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15657   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15658   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15659   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15660   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15661   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15662   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15663   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15664   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15665   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15666   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15667   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15668   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15669   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15670   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15671   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15672   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15673   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15674
15675   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15676   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15677
15678   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15679   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15680   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15681
15682   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15683   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15684
15685   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15686   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15687   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15688   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15689   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15690
15691   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15692   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15693
15694 #if 0
15695   ListNode *node = engine_snapshot_list_rnd;
15696   int num_bytes = 0;
15697
15698   while (node != NULL)
15699   {
15700     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15701
15702     node = node->next;
15703   }
15704
15705   Debug("game:playing:SaveEngineSnapshotBuffers",
15706         "size of engine snapshot: %d bytes", num_bytes);
15707 #endif
15708
15709   return buffers;
15710 }
15711
15712 void SaveEngineSnapshotSingle(void)
15713 {
15714   ListNode *buffers = SaveEngineSnapshotBuffers();
15715
15716   // finally save all snapshot buffers to single snapshot
15717   SaveSnapshotSingle(buffers);
15718
15719   // save level identification information
15720   setString(&snapshot_level_identifier, leveldir_current->identifier);
15721   snapshot_level_nr = level_nr;
15722 }
15723
15724 boolean CheckSaveEngineSnapshotToList(void)
15725 {
15726   boolean save_snapshot =
15727     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15728      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15729       game.snapshot.changed_action) ||
15730      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15731       game.snapshot.collected_item));
15732
15733   game.snapshot.changed_action = FALSE;
15734   game.snapshot.collected_item = FALSE;
15735   game.snapshot.save_snapshot = save_snapshot;
15736
15737   return save_snapshot;
15738 }
15739
15740 void SaveEngineSnapshotToList(void)
15741 {
15742   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15743       tape.quick_resume)
15744     return;
15745
15746   ListNode *buffers = SaveEngineSnapshotBuffers();
15747
15748   // finally save all snapshot buffers to snapshot list
15749   SaveSnapshotToList(buffers);
15750 }
15751
15752 void SaveEngineSnapshotToListInitial(void)
15753 {
15754   FreeEngineSnapshotList();
15755
15756   SaveEngineSnapshotToList();
15757 }
15758
15759 static void LoadEngineSnapshotValues(void)
15760 {
15761   // restore special values from snapshot structure
15762
15763   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15764     LoadEngineSnapshotValues_RND();
15765   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15766     LoadEngineSnapshotValues_EM();
15767   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15768     LoadEngineSnapshotValues_SP();
15769   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15770     LoadEngineSnapshotValues_MM();
15771 }
15772
15773 void LoadEngineSnapshotSingle(void)
15774 {
15775   LoadSnapshotSingle();
15776
15777   LoadEngineSnapshotValues();
15778 }
15779
15780 static void LoadEngineSnapshot_Undo(int steps)
15781 {
15782   LoadSnapshotFromList_Older(steps);
15783
15784   LoadEngineSnapshotValues();
15785 }
15786
15787 static void LoadEngineSnapshot_Redo(int steps)
15788 {
15789   LoadSnapshotFromList_Newer(steps);
15790
15791   LoadEngineSnapshotValues();
15792 }
15793
15794 boolean CheckEngineSnapshotSingle(void)
15795 {
15796   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15797           snapshot_level_nr == level_nr);
15798 }
15799
15800 boolean CheckEngineSnapshotList(void)
15801 {
15802   return CheckSnapshotList();
15803 }
15804
15805
15806 // ---------- new game button stuff -------------------------------------------
15807
15808 static struct
15809 {
15810   int graphic;
15811   struct XY *pos;
15812   int gadget_id;
15813   boolean *setup_value;
15814   boolean allowed_on_tape;
15815   boolean is_touch_button;
15816   char *infotext;
15817 } gamebutton_info[NUM_GAME_BUTTONS] =
15818 {
15819   {
15820     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15821     GAME_CTRL_ID_STOP,                          NULL,
15822     TRUE, FALSE,                                "stop game"
15823   },
15824   {
15825     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15826     GAME_CTRL_ID_PAUSE,                         NULL,
15827     TRUE, FALSE,                                "pause game"
15828   },
15829   {
15830     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15831     GAME_CTRL_ID_PLAY,                          NULL,
15832     TRUE, FALSE,                                "play game"
15833   },
15834   {
15835     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15836     GAME_CTRL_ID_UNDO,                          NULL,
15837     TRUE, FALSE,                                "undo step"
15838   },
15839   {
15840     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15841     GAME_CTRL_ID_REDO,                          NULL,
15842     TRUE, FALSE,                                "redo step"
15843   },
15844   {
15845     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15846     GAME_CTRL_ID_SAVE,                          NULL,
15847     TRUE, FALSE,                                "save game"
15848   },
15849   {
15850     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15851     GAME_CTRL_ID_PAUSE2,                        NULL,
15852     TRUE, FALSE,                                "pause game"
15853   },
15854   {
15855     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15856     GAME_CTRL_ID_LOAD,                          NULL,
15857     TRUE, FALSE,                                "load game"
15858   },
15859   {
15860     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15861     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15862     FALSE, FALSE,                               "stop game"
15863   },
15864   {
15865     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15866     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15867     FALSE, FALSE,                               "pause game"
15868   },
15869   {
15870     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15871     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15872     FALSE, FALSE,                               "play game"
15873   },
15874   {
15875     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
15876     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
15877     FALSE, TRUE,                                "stop game"
15878   },
15879   {
15880     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
15881     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
15882     FALSE, TRUE,                                "pause game"
15883   },
15884   {
15885     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15886     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15887     TRUE, FALSE,                                "background music on/off"
15888   },
15889   {
15890     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15891     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15892     TRUE, FALSE,                                "sound loops on/off"
15893   },
15894   {
15895     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15896     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15897     TRUE, FALSE,                                "normal sounds on/off"
15898   },
15899   {
15900     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15901     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15902     FALSE, FALSE,                               "background music on/off"
15903   },
15904   {
15905     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15906     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15907     FALSE, FALSE,                               "sound loops on/off"
15908   },
15909   {
15910     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15911     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15912     FALSE, FALSE,                               "normal sounds on/off"
15913   }
15914 };
15915
15916 void CreateGameButtons(void)
15917 {
15918   int i;
15919
15920   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15921   {
15922     int graphic = gamebutton_info[i].graphic;
15923     struct GraphicInfo *gfx = &graphic_info[graphic];
15924     struct XY *pos = gamebutton_info[i].pos;
15925     struct GadgetInfo *gi;
15926     int button_type;
15927     boolean checked;
15928     unsigned int event_mask;
15929     boolean is_touch_button = gamebutton_info[i].is_touch_button;
15930     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15931     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15932     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
15933     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
15934     int gd_x   = gfx->src_x;
15935     int gd_y   = gfx->src_y;
15936     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15937     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15938     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15939     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15940     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15941     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15942     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
15943     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
15944     int id = i;
15945
15946     if (gfx->bitmap == NULL)
15947     {
15948       game_gadget[id] = NULL;
15949
15950       continue;
15951     }
15952
15953     if (id == GAME_CTRL_ID_STOP ||
15954         id == GAME_CTRL_ID_PANEL_STOP ||
15955         id == GAME_CTRL_ID_TOUCH_STOP ||
15956         id == GAME_CTRL_ID_PLAY ||
15957         id == GAME_CTRL_ID_PANEL_PLAY ||
15958         id == GAME_CTRL_ID_SAVE ||
15959         id == GAME_CTRL_ID_LOAD)
15960     {
15961       button_type = GD_TYPE_NORMAL_BUTTON;
15962       checked = FALSE;
15963       event_mask = GD_EVENT_RELEASED;
15964     }
15965     else if (id == GAME_CTRL_ID_UNDO ||
15966              id == GAME_CTRL_ID_REDO)
15967     {
15968       button_type = GD_TYPE_NORMAL_BUTTON;
15969       checked = FALSE;
15970       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15971     }
15972     else
15973     {
15974       button_type = GD_TYPE_CHECK_BUTTON;
15975       checked = (gamebutton_info[i].setup_value != NULL ?
15976                  *gamebutton_info[i].setup_value : FALSE);
15977       event_mask = GD_EVENT_PRESSED;
15978     }
15979
15980     gi = CreateGadget(GDI_CUSTOM_ID, id,
15981                       GDI_IMAGE_ID, graphic,
15982                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15983                       GDI_X, base_x + x,
15984                       GDI_Y, base_y + y,
15985                       GDI_WIDTH, gfx->width,
15986                       GDI_HEIGHT, gfx->height,
15987                       GDI_TYPE, button_type,
15988                       GDI_STATE, GD_BUTTON_UNPRESSED,
15989                       GDI_CHECKED, checked,
15990                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15991                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15992                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15993                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15994                       GDI_DIRECT_DRAW, FALSE,
15995                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
15996                       GDI_EVENT_MASK, event_mask,
15997                       GDI_CALLBACK_ACTION, HandleGameButtons,
15998                       GDI_END);
15999
16000     if (gi == NULL)
16001       Fail("cannot create gadget");
16002
16003     game_gadget[id] = gi;
16004   }
16005 }
16006
16007 void FreeGameButtons(void)
16008 {
16009   int i;
16010
16011   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16012     FreeGadget(game_gadget[i]);
16013 }
16014
16015 static void UnmapGameButtonsAtSamePosition(int id)
16016 {
16017   int i;
16018
16019   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16020     if (i != id &&
16021         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16022         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16023       UnmapGadget(game_gadget[i]);
16024 }
16025
16026 static void UnmapGameButtonsAtSamePosition_All(void)
16027 {
16028   if (setup.show_snapshot_buttons)
16029   {
16030     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16031     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16032     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16033   }
16034   else
16035   {
16036     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16037     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16038     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16039
16040     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16041     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16042     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16043   }
16044 }
16045
16046 static void MapGameButtonsAtSamePosition(int id)
16047 {
16048   int i;
16049
16050   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16051     if (i != id &&
16052         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16053         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16054       MapGadget(game_gadget[i]);
16055
16056   UnmapGameButtonsAtSamePosition_All();
16057 }
16058
16059 void MapUndoRedoButtons(void)
16060 {
16061   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16062   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16063
16064   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16065   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16066 }
16067
16068 void UnmapUndoRedoButtons(void)
16069 {
16070   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16071   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16072
16073   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16074   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16075 }
16076
16077 void ModifyPauseButtons(void)
16078 {
16079   static int ids[] =
16080   {
16081     GAME_CTRL_ID_PAUSE,
16082     GAME_CTRL_ID_PAUSE2,
16083     GAME_CTRL_ID_PANEL_PAUSE,
16084     GAME_CTRL_ID_TOUCH_PAUSE,
16085     -1
16086   };
16087   int i;
16088
16089   for (i = 0; ids[i] > -1; i++)
16090     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16091 }
16092
16093 static void MapGameButtonsExt(boolean on_tape)
16094 {
16095   int i;
16096
16097   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16098     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
16099         i != GAME_CTRL_ID_UNDO &&
16100         i != GAME_CTRL_ID_REDO)
16101       MapGadget(game_gadget[i]);
16102
16103   UnmapGameButtonsAtSamePosition_All();
16104
16105   RedrawGameButtons();
16106 }
16107
16108 static void UnmapGameButtonsExt(boolean on_tape)
16109 {
16110   int i;
16111
16112   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16113     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16114       UnmapGadget(game_gadget[i]);
16115 }
16116
16117 static void RedrawGameButtonsExt(boolean on_tape)
16118 {
16119   int i;
16120
16121   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16122     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16123       RedrawGadget(game_gadget[i]);
16124 }
16125
16126 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16127 {
16128   if (gi == NULL)
16129     return;
16130
16131   gi->checked = state;
16132 }
16133
16134 static void RedrawSoundButtonGadget(int id)
16135 {
16136   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16137              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16138              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16139              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16140              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16141              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16142              id);
16143
16144   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16145   RedrawGadget(game_gadget[id2]);
16146 }
16147
16148 void MapGameButtons(void)
16149 {
16150   MapGameButtonsExt(FALSE);
16151 }
16152
16153 void UnmapGameButtons(void)
16154 {
16155   UnmapGameButtonsExt(FALSE);
16156 }
16157
16158 void RedrawGameButtons(void)
16159 {
16160   RedrawGameButtonsExt(FALSE);
16161 }
16162
16163 void MapGameButtonsOnTape(void)
16164 {
16165   MapGameButtonsExt(TRUE);
16166 }
16167
16168 void UnmapGameButtonsOnTape(void)
16169 {
16170   UnmapGameButtonsExt(TRUE);
16171 }
16172
16173 void RedrawGameButtonsOnTape(void)
16174 {
16175   RedrawGameButtonsExt(TRUE);
16176 }
16177
16178 static void GameUndoRedoExt(void)
16179 {
16180   ClearPlayerAction();
16181
16182   tape.pausing = TRUE;
16183
16184   RedrawPlayfield();
16185   UpdateAndDisplayGameControlValues();
16186
16187   DrawCompleteVideoDisplay();
16188   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16189   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16190   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16191
16192   BackToFront();
16193 }
16194
16195 static void GameUndo(int steps)
16196 {
16197   if (!CheckEngineSnapshotList())
16198     return;
16199
16200   LoadEngineSnapshot_Undo(steps);
16201
16202   GameUndoRedoExt();
16203 }
16204
16205 static void GameRedo(int steps)
16206 {
16207   if (!CheckEngineSnapshotList())
16208     return;
16209
16210   LoadEngineSnapshot_Redo(steps);
16211
16212   GameUndoRedoExt();
16213 }
16214
16215 static void HandleGameButtonsExt(int id, int button)
16216 {
16217   static boolean game_undo_executed = FALSE;
16218   int steps = BUTTON_STEPSIZE(button);
16219   boolean handle_game_buttons =
16220     (game_status == GAME_MODE_PLAYING ||
16221      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16222
16223   if (!handle_game_buttons)
16224     return;
16225
16226   switch (id)
16227   {
16228     case GAME_CTRL_ID_STOP:
16229     case GAME_CTRL_ID_PANEL_STOP:
16230     case GAME_CTRL_ID_TOUCH_STOP:
16231       if (game_status == GAME_MODE_MAIN)
16232         break;
16233
16234       if (tape.playing)
16235         TapeStop();
16236       else
16237         RequestQuitGame(TRUE);
16238
16239       break;
16240
16241     case GAME_CTRL_ID_PAUSE:
16242     case GAME_CTRL_ID_PAUSE2:
16243     case GAME_CTRL_ID_PANEL_PAUSE:
16244     case GAME_CTRL_ID_TOUCH_PAUSE:
16245       if (network.enabled && game_status == GAME_MODE_PLAYING)
16246       {
16247         if (tape.pausing)
16248           SendToServer_ContinuePlaying();
16249         else
16250           SendToServer_PausePlaying();
16251       }
16252       else
16253         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16254
16255       game_undo_executed = FALSE;
16256
16257       break;
16258
16259     case GAME_CTRL_ID_PLAY:
16260     case GAME_CTRL_ID_PANEL_PLAY:
16261       if (game_status == GAME_MODE_MAIN)
16262       {
16263         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16264       }
16265       else if (tape.pausing)
16266       {
16267         if (network.enabled)
16268           SendToServer_ContinuePlaying();
16269         else
16270           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16271       }
16272       break;
16273
16274     case GAME_CTRL_ID_UNDO:
16275       // Important: When using "save snapshot when collecting an item" mode,
16276       // load last (current) snapshot for first "undo" after pressing "pause"
16277       // (else the last-but-one snapshot would be loaded, because the snapshot
16278       // pointer already points to the last snapshot when pressing "pause",
16279       // which is fine for "every step/move" mode, but not for "every collect")
16280       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16281           !game_undo_executed)
16282         steps--;
16283
16284       game_undo_executed = TRUE;
16285
16286       GameUndo(steps);
16287       break;
16288
16289     case GAME_CTRL_ID_REDO:
16290       GameRedo(steps);
16291       break;
16292
16293     case GAME_CTRL_ID_SAVE:
16294       TapeQuickSave();
16295       break;
16296
16297     case GAME_CTRL_ID_LOAD:
16298       TapeQuickLoad();
16299       break;
16300
16301     case SOUND_CTRL_ID_MUSIC:
16302     case SOUND_CTRL_ID_PANEL_MUSIC:
16303       if (setup.sound_music)
16304       { 
16305         setup.sound_music = FALSE;
16306
16307         FadeMusic();
16308       }
16309       else if (audio.music_available)
16310       { 
16311         setup.sound = setup.sound_music = TRUE;
16312
16313         SetAudioMode(setup.sound);
16314
16315         if (game_status == GAME_MODE_PLAYING)
16316           PlayLevelMusic();
16317       }
16318
16319       RedrawSoundButtonGadget(id);
16320
16321       break;
16322
16323     case SOUND_CTRL_ID_LOOPS:
16324     case SOUND_CTRL_ID_PANEL_LOOPS:
16325       if (setup.sound_loops)
16326         setup.sound_loops = FALSE;
16327       else if (audio.loops_available)
16328       {
16329         setup.sound = setup.sound_loops = TRUE;
16330
16331         SetAudioMode(setup.sound);
16332       }
16333
16334       RedrawSoundButtonGadget(id);
16335
16336       break;
16337
16338     case SOUND_CTRL_ID_SIMPLE:
16339     case SOUND_CTRL_ID_PANEL_SIMPLE:
16340       if (setup.sound_simple)
16341         setup.sound_simple = FALSE;
16342       else if (audio.sound_available)
16343       {
16344         setup.sound = setup.sound_simple = TRUE;
16345
16346         SetAudioMode(setup.sound);
16347       }
16348
16349       RedrawSoundButtonGadget(id);
16350
16351       break;
16352
16353     default:
16354       break;
16355   }
16356 }
16357
16358 static void HandleGameButtons(struct GadgetInfo *gi)
16359 {
16360   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16361 }
16362
16363 void HandleSoundButtonKeys(Key key)
16364 {
16365   if (key == setup.shortcut.sound_simple)
16366     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16367   else if (key == setup.shortcut.sound_loops)
16368     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16369   else if (key == setup.shortcut.sound_music)
16370     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16371 }