c4819c1190a382a0d52d8a7962cc4dd34415275e
[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 struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1123
1124 // for detection of endless loops, caused by custom element programming
1125 // (using maximal playfield width x 10 is just a rough approximation)
1126 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1127
1128 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1129 {                                                                       \
1130   if (recursion_loop_detected)                                          \
1131     return (rc);                                                        \
1132                                                                         \
1133   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1134   {                                                                     \
1135     recursion_loop_detected = TRUE;                                     \
1136     recursion_loop_element = (e);                                       \
1137   }                                                                     \
1138                                                                         \
1139   recursion_loop_depth++;                                               \
1140 }
1141
1142 #define RECURSION_LOOP_DETECTION_END()                                  \
1143 {                                                                       \
1144   recursion_loop_depth--;                                               \
1145 }
1146
1147 static int recursion_loop_depth;
1148 static boolean recursion_loop_detected;
1149 static boolean recursion_loop_element;
1150
1151 static int map_player_action[MAX_PLAYERS];
1152
1153
1154 // ----------------------------------------------------------------------------
1155 // definition of elements that automatically change to other elements after
1156 // a specified time, eventually calling a function when changing
1157 // ----------------------------------------------------------------------------
1158
1159 // forward declaration for changer functions
1160 static void InitBuggyBase(int, int);
1161 static void WarnBuggyBase(int, int);
1162
1163 static void InitTrap(int, int);
1164 static void ActivateTrap(int, int);
1165 static void ChangeActiveTrap(int, int);
1166
1167 static void InitRobotWheel(int, int);
1168 static void RunRobotWheel(int, int);
1169 static void StopRobotWheel(int, int);
1170
1171 static void InitTimegateWheel(int, int);
1172 static void RunTimegateWheel(int, int);
1173
1174 static void InitMagicBallDelay(int, int);
1175 static void ActivateMagicBall(int, int);
1176
1177 struct ChangingElementInfo
1178 {
1179   int element;
1180   int target_element;
1181   int change_delay;
1182   void (*pre_change_function)(int x, int y);
1183   void (*change_function)(int x, int y);
1184   void (*post_change_function)(int x, int y);
1185 };
1186
1187 static struct ChangingElementInfo change_delay_list[] =
1188 {
1189   {
1190     EL_NUT_BREAKING,
1191     EL_EMERALD,
1192     6,
1193     NULL,
1194     NULL,
1195     NULL
1196   },
1197   {
1198     EL_PEARL_BREAKING,
1199     EL_EMPTY,
1200     8,
1201     NULL,
1202     NULL,
1203     NULL
1204   },
1205   {
1206     EL_EXIT_OPENING,
1207     EL_EXIT_OPEN,
1208     29,
1209     NULL,
1210     NULL,
1211     NULL
1212   },
1213   {
1214     EL_EXIT_CLOSING,
1215     EL_EXIT_CLOSED,
1216     29,
1217     NULL,
1218     NULL,
1219     NULL
1220   },
1221   {
1222     EL_STEEL_EXIT_OPENING,
1223     EL_STEEL_EXIT_OPEN,
1224     29,
1225     NULL,
1226     NULL,
1227     NULL
1228   },
1229   {
1230     EL_STEEL_EXIT_CLOSING,
1231     EL_STEEL_EXIT_CLOSED,
1232     29,
1233     NULL,
1234     NULL,
1235     NULL
1236   },
1237   {
1238     EL_EM_EXIT_OPENING,
1239     EL_EM_EXIT_OPEN,
1240     29,
1241     NULL,
1242     NULL,
1243     NULL
1244   },
1245   {
1246     EL_EM_EXIT_CLOSING,
1247     EL_EMPTY,
1248     29,
1249     NULL,
1250     NULL,
1251     NULL
1252   },
1253   {
1254     EL_EM_STEEL_EXIT_OPENING,
1255     EL_EM_STEEL_EXIT_OPEN,
1256     29,
1257     NULL,
1258     NULL,
1259     NULL
1260   },
1261   {
1262     EL_EM_STEEL_EXIT_CLOSING,
1263     EL_STEELWALL,
1264     29,
1265     NULL,
1266     NULL,
1267     NULL
1268   },
1269   {
1270     EL_SP_EXIT_OPENING,
1271     EL_SP_EXIT_OPEN,
1272     29,
1273     NULL,
1274     NULL,
1275     NULL
1276   },
1277   {
1278     EL_SP_EXIT_CLOSING,
1279     EL_SP_EXIT_CLOSED,
1280     29,
1281     NULL,
1282     NULL,
1283     NULL
1284   },
1285   {
1286     EL_SWITCHGATE_OPENING,
1287     EL_SWITCHGATE_OPEN,
1288     29,
1289     NULL,
1290     NULL,
1291     NULL
1292   },
1293   {
1294     EL_SWITCHGATE_CLOSING,
1295     EL_SWITCHGATE_CLOSED,
1296     29,
1297     NULL,
1298     NULL,
1299     NULL
1300   },
1301   {
1302     EL_TIMEGATE_OPENING,
1303     EL_TIMEGATE_OPEN,
1304     29,
1305     NULL,
1306     NULL,
1307     NULL
1308   },
1309   {
1310     EL_TIMEGATE_CLOSING,
1311     EL_TIMEGATE_CLOSED,
1312     29,
1313     NULL,
1314     NULL,
1315     NULL
1316   },
1317
1318   {
1319     EL_ACID_SPLASH_LEFT,
1320     EL_EMPTY,
1321     8,
1322     NULL,
1323     NULL,
1324     NULL
1325   },
1326   {
1327     EL_ACID_SPLASH_RIGHT,
1328     EL_EMPTY,
1329     8,
1330     NULL,
1331     NULL,
1332     NULL
1333   },
1334   {
1335     EL_SP_BUGGY_BASE,
1336     EL_SP_BUGGY_BASE_ACTIVATING,
1337     0,
1338     InitBuggyBase,
1339     NULL,
1340     NULL
1341   },
1342   {
1343     EL_SP_BUGGY_BASE_ACTIVATING,
1344     EL_SP_BUGGY_BASE_ACTIVE,
1345     0,
1346     InitBuggyBase,
1347     NULL,
1348     NULL
1349   },
1350   {
1351     EL_SP_BUGGY_BASE_ACTIVE,
1352     EL_SP_BUGGY_BASE,
1353     0,
1354     InitBuggyBase,
1355     WarnBuggyBase,
1356     NULL
1357   },
1358   {
1359     EL_TRAP,
1360     EL_TRAP_ACTIVE,
1361     0,
1362     InitTrap,
1363     NULL,
1364     ActivateTrap
1365   },
1366   {
1367     EL_TRAP_ACTIVE,
1368     EL_TRAP,
1369     31,
1370     NULL,
1371     ChangeActiveTrap,
1372     NULL
1373   },
1374   {
1375     EL_ROBOT_WHEEL_ACTIVE,
1376     EL_ROBOT_WHEEL,
1377     0,
1378     InitRobotWheel,
1379     RunRobotWheel,
1380     StopRobotWheel
1381   },
1382   {
1383     EL_TIMEGATE_SWITCH_ACTIVE,
1384     EL_TIMEGATE_SWITCH,
1385     0,
1386     InitTimegateWheel,
1387     RunTimegateWheel,
1388     NULL
1389   },
1390   {
1391     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1392     EL_DC_TIMEGATE_SWITCH,
1393     0,
1394     InitTimegateWheel,
1395     RunTimegateWheel,
1396     NULL
1397   },
1398   {
1399     EL_EMC_MAGIC_BALL_ACTIVE,
1400     EL_EMC_MAGIC_BALL_ACTIVE,
1401     0,
1402     InitMagicBallDelay,
1403     NULL,
1404     ActivateMagicBall
1405   },
1406   {
1407     EL_EMC_SPRING_BUMPER_ACTIVE,
1408     EL_EMC_SPRING_BUMPER,
1409     8,
1410     NULL,
1411     NULL,
1412     NULL
1413   },
1414   {
1415     EL_DIAGONAL_SHRINKING,
1416     EL_UNDEFINED,
1417     0,
1418     NULL,
1419     NULL,
1420     NULL
1421   },
1422   {
1423     EL_DIAGONAL_GROWING,
1424     EL_UNDEFINED,
1425     0,
1426     NULL,
1427     NULL,
1428     NULL,
1429   },
1430
1431   {
1432     EL_UNDEFINED,
1433     EL_UNDEFINED,
1434     -1,
1435     NULL,
1436     NULL,
1437     NULL
1438   }
1439 };
1440
1441 struct
1442 {
1443   int element;
1444   int push_delay_fixed, push_delay_random;
1445 }
1446 push_delay_list[] =
1447 {
1448   { EL_SPRING,                  0, 0 },
1449   { EL_BALLOON,                 0, 0 },
1450
1451   { EL_SOKOBAN_OBJECT,          2, 0 },
1452   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1453   { EL_SATELLITE,               2, 0 },
1454   { EL_SP_DISK_YELLOW,          2, 0 },
1455
1456   { EL_UNDEFINED,               0, 0 },
1457 };
1458
1459 struct
1460 {
1461   int element;
1462   int move_stepsize;
1463 }
1464 move_stepsize_list[] =
1465 {
1466   { EL_AMOEBA_DROP,             2 },
1467   { EL_AMOEBA_DROPPING,         2 },
1468   { EL_QUICKSAND_FILLING,       1 },
1469   { EL_QUICKSAND_EMPTYING,      1 },
1470   { EL_QUICKSAND_FAST_FILLING,  2 },
1471   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1472   { EL_MAGIC_WALL_FILLING,      2 },
1473   { EL_MAGIC_WALL_EMPTYING,     2 },
1474   { EL_BD_MAGIC_WALL_FILLING,   2 },
1475   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1476   { EL_DC_MAGIC_WALL_FILLING,   2 },
1477   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1478
1479   { EL_UNDEFINED,               0 },
1480 };
1481
1482 struct
1483 {
1484   int element;
1485   int count;
1486 }
1487 collect_count_list[] =
1488 {
1489   { EL_EMERALD,                 1 },
1490   { EL_BD_DIAMOND,              1 },
1491   { EL_EMERALD_YELLOW,          1 },
1492   { EL_EMERALD_RED,             1 },
1493   { EL_EMERALD_PURPLE,          1 },
1494   { EL_DIAMOND,                 3 },
1495   { EL_SP_INFOTRON,             1 },
1496   { EL_PEARL,                   5 },
1497   { EL_CRYSTAL,                 8 },
1498
1499   { EL_UNDEFINED,               0 },
1500 };
1501
1502 struct
1503 {
1504   int element;
1505   int direction;
1506 }
1507 access_direction_list[] =
1508 {
1509   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1510   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1511   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1512   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1513   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1514   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1515   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1516   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1517   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1518   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1519   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1520
1521   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1522   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1523   { EL_SP_PORT_UP,                                                   MV_DOWN },
1524   { EL_SP_PORT_DOWN,                                         MV_UP           },
1525   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1526   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1527   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1528   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1529   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1530   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1531   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1532   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1533   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1534   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1535   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1536   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1537   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1538   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1539   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1540
1541   { EL_UNDEFINED,                       MV_NONE                              }
1542 };
1543
1544 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1545
1546 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1547 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1548 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1549                                  IS_JUST_CHANGING(x, y))
1550
1551 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1552
1553 // static variables for playfield scan mode (scanning forward or backward)
1554 static int playfield_scan_start_x = 0;
1555 static int playfield_scan_start_y = 0;
1556 static int playfield_scan_delta_x = 1;
1557 static int playfield_scan_delta_y = 1;
1558
1559 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1560                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1561                                      (y) += playfield_scan_delta_y)     \
1562                                 for ((x) = playfield_scan_start_x;      \
1563                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1564                                      (x) += playfield_scan_delta_x)
1565
1566 #ifdef DEBUG
1567 void DEBUG_SetMaximumDynamite(void)
1568 {
1569   int i;
1570
1571   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1572     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1573       local_player->inventory_element[local_player->inventory_size++] =
1574         EL_DYNAMITE;
1575 }
1576 #endif
1577
1578 static void InitPlayfieldScanModeVars(void)
1579 {
1580   if (game.use_reverse_scan_direction)
1581   {
1582     playfield_scan_start_x = lev_fieldx - 1;
1583     playfield_scan_start_y = lev_fieldy - 1;
1584
1585     playfield_scan_delta_x = -1;
1586     playfield_scan_delta_y = -1;
1587   }
1588   else
1589   {
1590     playfield_scan_start_x = 0;
1591     playfield_scan_start_y = 0;
1592
1593     playfield_scan_delta_x = 1;
1594     playfield_scan_delta_y = 1;
1595   }
1596 }
1597
1598 static void InitPlayfieldScanMode(int mode)
1599 {
1600   game.use_reverse_scan_direction =
1601     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1602
1603   InitPlayfieldScanModeVars();
1604 }
1605
1606 static int get_move_delay_from_stepsize(int move_stepsize)
1607 {
1608   move_stepsize =
1609     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1610
1611   // make sure that stepsize value is always a power of 2
1612   move_stepsize = (1 << log_2(move_stepsize));
1613
1614   return TILEX / move_stepsize;
1615 }
1616
1617 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1618                                boolean init_game)
1619 {
1620   int player_nr = player->index_nr;
1621   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1622   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1623
1624   // do no immediately change move delay -- the player might just be moving
1625   player->move_delay_value_next = move_delay;
1626
1627   // information if player can move must be set separately
1628   player->cannot_move = cannot_move;
1629
1630   if (init_game)
1631   {
1632     player->move_delay       = game.initial_move_delay[player_nr];
1633     player->move_delay_value = game.initial_move_delay_value[player_nr];
1634
1635     player->move_delay_value_next = -1;
1636
1637     player->move_delay_reset_counter = 0;
1638   }
1639 }
1640
1641 void GetPlayerConfig(void)
1642 {
1643   GameFrameDelay = setup.game_frame_delay;
1644
1645   if (!audio.sound_available)
1646     setup.sound_simple = FALSE;
1647
1648   if (!audio.loops_available)
1649     setup.sound_loops = FALSE;
1650
1651   if (!audio.music_available)
1652     setup.sound_music = FALSE;
1653
1654   if (!video.fullscreen_available)
1655     setup.fullscreen = FALSE;
1656
1657   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1658
1659   SetAudioMode(setup.sound);
1660 }
1661
1662 int GetElementFromGroupElement(int element)
1663 {
1664   if (IS_GROUP_ELEMENT(element))
1665   {
1666     struct ElementGroupInfo *group = element_info[element].group;
1667     int last_anim_random_frame = gfx.anim_random_frame;
1668     int element_pos;
1669
1670     if (group->choice_mode == ANIM_RANDOM)
1671       gfx.anim_random_frame = RND(group->num_elements_resolved);
1672
1673     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1674                                     group->choice_mode, 0,
1675                                     group->choice_pos);
1676
1677     if (group->choice_mode == ANIM_RANDOM)
1678       gfx.anim_random_frame = last_anim_random_frame;
1679
1680     group->choice_pos++;
1681
1682     element = group->element_resolved[element_pos];
1683   }
1684
1685   return element;
1686 }
1687
1688 static void IncrementSokobanFieldsNeeded(void)
1689 {
1690   if (level.sb_fields_needed)
1691     game.sokoban_fields_still_needed++;
1692 }
1693
1694 static void IncrementSokobanObjectsNeeded(void)
1695 {
1696   if (level.sb_objects_needed)
1697     game.sokoban_objects_still_needed++;
1698 }
1699
1700 static void DecrementSokobanFieldsNeeded(void)
1701 {
1702   if (game.sokoban_fields_still_needed > 0)
1703     game.sokoban_fields_still_needed--;
1704 }
1705
1706 static void DecrementSokobanObjectsNeeded(void)
1707 {
1708   if (game.sokoban_objects_still_needed > 0)
1709     game.sokoban_objects_still_needed--;
1710 }
1711
1712 static void InitPlayerField(int x, int y, int element, boolean init_game)
1713 {
1714   if (element == EL_SP_MURPHY)
1715   {
1716     if (init_game)
1717     {
1718       if (stored_player[0].present)
1719       {
1720         Tile[x][y] = EL_SP_MURPHY_CLONE;
1721
1722         return;
1723       }
1724       else
1725       {
1726         stored_player[0].initial_element = element;
1727         stored_player[0].use_murphy = TRUE;
1728
1729         if (!level.use_artwork_element[0])
1730           stored_player[0].artwork_element = EL_SP_MURPHY;
1731       }
1732
1733       Tile[x][y] = EL_PLAYER_1;
1734     }
1735   }
1736
1737   if (init_game)
1738   {
1739     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1740     int jx = player->jx, jy = player->jy;
1741
1742     player->present = TRUE;
1743
1744     player->block_last_field = (element == EL_SP_MURPHY ?
1745                                 level.sp_block_last_field :
1746                                 level.block_last_field);
1747
1748     // ---------- initialize player's last field block delay ------------------
1749
1750     // always start with reliable default value (no adjustment needed)
1751     player->block_delay_adjustment = 0;
1752
1753     // special case 1: in Supaplex, Murphy blocks last field one more frame
1754     if (player->block_last_field && element == EL_SP_MURPHY)
1755       player->block_delay_adjustment = 1;
1756
1757     // special case 2: in game engines before 3.1.1, blocking was different
1758     if (game.use_block_last_field_bug)
1759       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1760
1761     if (!network.enabled || player->connected_network)
1762     {
1763       player->active = TRUE;
1764
1765       // remove potentially duplicate players
1766       if (StorePlayer[jx][jy] == Tile[x][y])
1767         StorePlayer[jx][jy] = 0;
1768
1769       StorePlayer[x][y] = Tile[x][y];
1770
1771 #if DEBUG_INIT_PLAYER
1772       Debug("game:init:player", "- player element %d activated",
1773             player->element_nr);
1774       Debug("game:init:player", "  (local player is %d and currently %s)",
1775             local_player->element_nr,
1776             local_player->active ? "active" : "not active");
1777     }
1778 #endif
1779
1780     Tile[x][y] = EL_EMPTY;
1781
1782     player->jx = player->last_jx = x;
1783     player->jy = player->last_jy = y;
1784   }
1785
1786   // always check if player was just killed and should be reanimated
1787   {
1788     int player_nr = GET_PLAYER_NR(element);
1789     struct PlayerInfo *player = &stored_player[player_nr];
1790
1791     if (player->active && player->killed)
1792       player->reanimated = TRUE; // if player was just killed, reanimate him
1793   }
1794 }
1795
1796 static void InitField(int x, int y, boolean init_game)
1797 {
1798   int element = Tile[x][y];
1799
1800   switch (element)
1801   {
1802     case EL_SP_MURPHY:
1803     case EL_PLAYER_1:
1804     case EL_PLAYER_2:
1805     case EL_PLAYER_3:
1806     case EL_PLAYER_4:
1807       InitPlayerField(x, y, element, init_game);
1808       break;
1809
1810     case EL_SOKOBAN_FIELD_PLAYER:
1811       element = Tile[x][y] = EL_PLAYER_1;
1812       InitField(x, y, init_game);
1813
1814       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1815       InitField(x, y, init_game);
1816       break;
1817
1818     case EL_SOKOBAN_FIELD_EMPTY:
1819       IncrementSokobanFieldsNeeded();
1820       break;
1821
1822     case EL_SOKOBAN_OBJECT:
1823       IncrementSokobanObjectsNeeded();
1824       break;
1825
1826     case EL_STONEBLOCK:
1827       if (x < lev_fieldx-1 && Tile[x+1][y] == EL_ACID)
1828         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1829       else if (x > 0 && Tile[x-1][y] == EL_ACID)
1830         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1831       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPLEFT)
1832         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1833       else if (y > 0 && Tile[x][y-1] == EL_ACID)
1834         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1835       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1836         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1837       break;
1838
1839     case EL_BUG:
1840     case EL_BUG_RIGHT:
1841     case EL_BUG_UP:
1842     case EL_BUG_LEFT:
1843     case EL_BUG_DOWN:
1844     case EL_SPACESHIP:
1845     case EL_SPACESHIP_RIGHT:
1846     case EL_SPACESHIP_UP:
1847     case EL_SPACESHIP_LEFT:
1848     case EL_SPACESHIP_DOWN:
1849     case EL_BD_BUTTERFLY:
1850     case EL_BD_BUTTERFLY_RIGHT:
1851     case EL_BD_BUTTERFLY_UP:
1852     case EL_BD_BUTTERFLY_LEFT:
1853     case EL_BD_BUTTERFLY_DOWN:
1854     case EL_BD_FIREFLY:
1855     case EL_BD_FIREFLY_RIGHT:
1856     case EL_BD_FIREFLY_UP:
1857     case EL_BD_FIREFLY_LEFT:
1858     case EL_BD_FIREFLY_DOWN:
1859     case EL_PACMAN_RIGHT:
1860     case EL_PACMAN_UP:
1861     case EL_PACMAN_LEFT:
1862     case EL_PACMAN_DOWN:
1863     case EL_YAMYAM:
1864     case EL_YAMYAM_LEFT:
1865     case EL_YAMYAM_RIGHT:
1866     case EL_YAMYAM_UP:
1867     case EL_YAMYAM_DOWN:
1868     case EL_DARK_YAMYAM:
1869     case EL_ROBOT:
1870     case EL_PACMAN:
1871     case EL_SP_SNIKSNAK:
1872     case EL_SP_ELECTRON:
1873     case EL_MOLE:
1874     case EL_MOLE_LEFT:
1875     case EL_MOLE_RIGHT:
1876     case EL_MOLE_UP:
1877     case EL_MOLE_DOWN:
1878     case EL_SPRING_LEFT:
1879     case EL_SPRING_RIGHT:
1880       InitMovDir(x, y);
1881       break;
1882
1883     case EL_AMOEBA_FULL:
1884     case EL_BD_AMOEBA:
1885       InitAmoebaNr(x, y);
1886       break;
1887
1888     case EL_AMOEBA_DROP:
1889       if (y == lev_fieldy - 1)
1890       {
1891         Tile[x][y] = EL_AMOEBA_GROWING;
1892         Store[x][y] = EL_AMOEBA_WET;
1893       }
1894       break;
1895
1896     case EL_DYNAMITE_ACTIVE:
1897     case EL_SP_DISK_RED_ACTIVE:
1898     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1899     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1900     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1901     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1902       MovDelay[x][y] = 96;
1903       break;
1904
1905     case EL_EM_DYNAMITE_ACTIVE:
1906       MovDelay[x][y] = 32;
1907       break;
1908
1909     case EL_LAMP:
1910       game.lights_still_needed++;
1911       break;
1912
1913     case EL_PENGUIN:
1914       game.friends_still_needed++;
1915       break;
1916
1917     case EL_PIG:
1918     case EL_DRAGON:
1919       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1920       break;
1921
1922     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1923     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1924     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1925     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1926     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1927     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1928     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1929     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1930     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1931     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1932     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1933     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1934       if (init_game)
1935       {
1936         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1937         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1938         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1939
1940         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1941         {
1942           game.belt_dir[belt_nr] = belt_dir;
1943           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1944         }
1945         else    // more than one switch -- set it like the first switch
1946         {
1947           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1948         }
1949       }
1950       break;
1951
1952     case EL_LIGHT_SWITCH_ACTIVE:
1953       if (init_game)
1954         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1955       break;
1956
1957     case EL_INVISIBLE_STEELWALL:
1958     case EL_INVISIBLE_WALL:
1959     case EL_INVISIBLE_SAND:
1960       if (game.light_time_left > 0 ||
1961           game.lenses_time_left > 0)
1962         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1963       break;
1964
1965     case EL_EMC_MAGIC_BALL:
1966       if (game.ball_active)
1967         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1968       break;
1969
1970     case EL_EMC_MAGIC_BALL_SWITCH:
1971       if (game.ball_active)
1972         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1973       break;
1974
1975     case EL_TRIGGER_PLAYER:
1976     case EL_TRIGGER_ELEMENT:
1977     case EL_TRIGGER_CE_VALUE:
1978     case EL_TRIGGER_CE_SCORE:
1979     case EL_SELF:
1980     case EL_ANY_ELEMENT:
1981     case EL_CURRENT_CE_VALUE:
1982     case EL_CURRENT_CE_SCORE:
1983     case EL_PREV_CE_1:
1984     case EL_PREV_CE_2:
1985     case EL_PREV_CE_3:
1986     case EL_PREV_CE_4:
1987     case EL_PREV_CE_5:
1988     case EL_PREV_CE_6:
1989     case EL_PREV_CE_7:
1990     case EL_PREV_CE_8:
1991     case EL_NEXT_CE_1:
1992     case EL_NEXT_CE_2:
1993     case EL_NEXT_CE_3:
1994     case EL_NEXT_CE_4:
1995     case EL_NEXT_CE_5:
1996     case EL_NEXT_CE_6:
1997     case EL_NEXT_CE_7:
1998     case EL_NEXT_CE_8:
1999       // reference elements should not be used on the playfield
2000       Tile[x][y] = EL_EMPTY;
2001       break;
2002
2003     default:
2004       if (IS_CUSTOM_ELEMENT(element))
2005       {
2006         if (CAN_MOVE(element))
2007           InitMovDir(x, y);
2008
2009         if (!element_info[element].use_last_ce_value || init_game)
2010           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2011       }
2012       else if (IS_GROUP_ELEMENT(element))
2013       {
2014         Tile[x][y] = GetElementFromGroupElement(element);
2015
2016         InitField(x, y, init_game);
2017       }
2018
2019       break;
2020   }
2021
2022   if (!init_game)
2023     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2024 }
2025
2026 static void InitField_WithBug1(int x, int y, boolean init_game)
2027 {
2028   InitField(x, y, init_game);
2029
2030   // not needed to call InitMovDir() -- already done by InitField()!
2031   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2032       CAN_MOVE(Tile[x][y]))
2033     InitMovDir(x, y);
2034 }
2035
2036 static void InitField_WithBug2(int x, int y, boolean init_game)
2037 {
2038   int old_element = Tile[x][y];
2039
2040   InitField(x, y, init_game);
2041
2042   // not needed to call InitMovDir() -- already done by InitField()!
2043   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2044       CAN_MOVE(old_element) &&
2045       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2046     InitMovDir(x, y);
2047
2048   /* this case is in fact a combination of not less than three bugs:
2049      first, it calls InitMovDir() for elements that can move, although this is
2050      already done by InitField(); then, it checks the element that was at this
2051      field _before_ the call to InitField() (which can change it); lastly, it
2052      was not called for "mole with direction" elements, which were treated as
2053      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2054   */
2055 }
2056
2057 static int get_key_element_from_nr(int key_nr)
2058 {
2059   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2060                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2061                           EL_EM_KEY_1 : EL_KEY_1);
2062
2063   return key_base_element + key_nr;
2064 }
2065
2066 static int get_next_dropped_element(struct PlayerInfo *player)
2067 {
2068   return (player->inventory_size > 0 ?
2069           player->inventory_element[player->inventory_size - 1] :
2070           player->inventory_infinite_element != EL_UNDEFINED ?
2071           player->inventory_infinite_element :
2072           player->dynabombs_left > 0 ?
2073           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2074           EL_UNDEFINED);
2075 }
2076
2077 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2078 {
2079   // pos >= 0: get element from bottom of the stack;
2080   // pos <  0: get element from top of the stack
2081
2082   if (pos < 0)
2083   {
2084     int min_inventory_size = -pos;
2085     int inventory_pos = player->inventory_size - min_inventory_size;
2086     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2087
2088     return (player->inventory_size >= min_inventory_size ?
2089             player->inventory_element[inventory_pos] :
2090             player->inventory_infinite_element != EL_UNDEFINED ?
2091             player->inventory_infinite_element :
2092             player->dynabombs_left >= min_dynabombs_left ?
2093             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2094             EL_UNDEFINED);
2095   }
2096   else
2097   {
2098     int min_dynabombs_left = pos + 1;
2099     int min_inventory_size = pos + 1 - player->dynabombs_left;
2100     int inventory_pos = pos - player->dynabombs_left;
2101
2102     return (player->inventory_infinite_element != EL_UNDEFINED ?
2103             player->inventory_infinite_element :
2104             player->dynabombs_left >= min_dynabombs_left ?
2105             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2106             player->inventory_size >= min_inventory_size ?
2107             player->inventory_element[inventory_pos] :
2108             EL_UNDEFINED);
2109   }
2110 }
2111
2112 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2113 {
2114   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2115   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2116   int compare_result;
2117
2118   if (gpo1->sort_priority != gpo2->sort_priority)
2119     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2120   else
2121     compare_result = gpo1->nr - gpo2->nr;
2122
2123   return compare_result;
2124 }
2125
2126 int getPlayerInventorySize(int player_nr)
2127 {
2128   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2129     return game_em.ply[player_nr]->dynamite;
2130   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2131     return game_sp.red_disk_count;
2132   else
2133     return stored_player[player_nr].inventory_size;
2134 }
2135
2136 static void InitGameControlValues(void)
2137 {
2138   int i;
2139
2140   for (i = 0; game_panel_controls[i].nr != -1; i++)
2141   {
2142     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2143     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2144     struct TextPosInfo *pos = gpc->pos;
2145     int nr = gpc->nr;
2146     int type = gpc->type;
2147
2148     if (nr != i)
2149     {
2150       Error("'game_panel_controls' structure corrupted at %d", i);
2151
2152       Fail("this should not happen -- please debug");
2153     }
2154
2155     // force update of game controls after initialization
2156     gpc->value = gpc->last_value = -1;
2157     gpc->frame = gpc->last_frame = -1;
2158     gpc->gfx_frame = -1;
2159
2160     // determine panel value width for later calculation of alignment
2161     if (type == TYPE_INTEGER || type == TYPE_STRING)
2162     {
2163       pos->width = pos->size * getFontWidth(pos->font);
2164       pos->height = getFontHeight(pos->font);
2165     }
2166     else if (type == TYPE_ELEMENT)
2167     {
2168       pos->width = pos->size;
2169       pos->height = pos->size;
2170     }
2171
2172     // fill structure for game panel draw order
2173     gpo->nr = gpc->nr;
2174     gpo->sort_priority = pos->sort_priority;
2175   }
2176
2177   // sort game panel controls according to sort_priority and control number
2178   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2179         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2180 }
2181
2182 static void UpdatePlayfieldElementCount(void)
2183 {
2184   boolean use_element_count = FALSE;
2185   int i, j, x, y;
2186
2187   // first check if it is needed at all to calculate playfield element count
2188   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2189     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2190       use_element_count = TRUE;
2191
2192   if (!use_element_count)
2193     return;
2194
2195   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2196     element_info[i].element_count = 0;
2197
2198   SCAN_PLAYFIELD(x, y)
2199   {
2200     element_info[Tile[x][y]].element_count++;
2201   }
2202
2203   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2204     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2205       if (IS_IN_GROUP(j, i))
2206         element_info[EL_GROUP_START + i].element_count +=
2207           element_info[j].element_count;
2208 }
2209
2210 static void UpdateGameControlValues(void)
2211 {
2212   int i, k;
2213   int time = (game.LevelSolved ?
2214               game.LevelSolved_CountingTime :
2215               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2216               game_em.lev->time :
2217               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2218               game_sp.time_played :
2219               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2220               game_mm.energy_left :
2221               game.no_time_limit ? TimePlayed : TimeLeft);
2222   int score = (game.LevelSolved ?
2223                game.LevelSolved_CountingScore :
2224                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2225                game_em.lev->score :
2226                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2227                game_sp.score :
2228                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2229                game_mm.score :
2230                game.score);
2231   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2232               game_em.lev->gems_needed :
2233               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2234               game_sp.infotrons_still_needed :
2235               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2236               game_mm.kettles_still_needed :
2237               game.gems_still_needed);
2238   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2239                      game_em.lev->gems_needed > 0 :
2240                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2241                      game_sp.infotrons_still_needed > 0 :
2242                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2243                      game_mm.kettles_still_needed > 0 ||
2244                      game_mm.lights_still_needed > 0 :
2245                      game.gems_still_needed > 0 ||
2246                      game.sokoban_fields_still_needed > 0 ||
2247                      game.sokoban_objects_still_needed > 0 ||
2248                      game.lights_still_needed > 0);
2249   int health = (game.LevelSolved ?
2250                 game.LevelSolved_CountingHealth :
2251                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2252                 MM_HEALTH(game_mm.laser_overload_value) :
2253                 game.health);
2254   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2255
2256   UpdatePlayfieldElementCount();
2257
2258   // update game panel control values
2259
2260   // used instead of "level_nr" (for network games)
2261   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2262   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2263
2264   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2265   for (i = 0; i < MAX_NUM_KEYS; i++)
2266     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2267   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2268   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2269
2270   if (game.centered_player_nr == -1)
2271   {
2272     for (i = 0; i < MAX_PLAYERS; i++)
2273     {
2274       // only one player in Supaplex game engine
2275       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2276         break;
2277
2278       for (k = 0; k < MAX_NUM_KEYS; k++)
2279       {
2280         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2281         {
2282           if (game_em.ply[i]->keys & (1 << k))
2283             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2284               get_key_element_from_nr(k);
2285         }
2286         else if (stored_player[i].key[k])
2287           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2288             get_key_element_from_nr(k);
2289       }
2290
2291       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2292         getPlayerInventorySize(i);
2293
2294       if (stored_player[i].num_white_keys > 0)
2295         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2296           EL_DC_KEY_WHITE;
2297
2298       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2299         stored_player[i].num_white_keys;
2300     }
2301   }
2302   else
2303   {
2304     int player_nr = game.centered_player_nr;
2305
2306     for (k = 0; k < MAX_NUM_KEYS; k++)
2307     {
2308       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2309       {
2310         if (game_em.ply[player_nr]->keys & (1 << k))
2311           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2312             get_key_element_from_nr(k);
2313       }
2314       else if (stored_player[player_nr].key[k])
2315         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2316           get_key_element_from_nr(k);
2317     }
2318
2319     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2320       getPlayerInventorySize(player_nr);
2321
2322     if (stored_player[player_nr].num_white_keys > 0)
2323       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2324
2325     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2326       stored_player[player_nr].num_white_keys;
2327   }
2328
2329   // try to display as many collected keys as possible in the default game panel
2330   for (i = STD_NUM_KEYS; i < MAX_NUM_KEYS + 1; i++)     // EMC keys + white key
2331   {
2332     int nr = GAME_PANEL_KEY_1 + i;
2333     int emc_key = get_key_element_from_nr(i);
2334     int element = (i < MAX_NUM_KEYS ? emc_key : EL_DC_KEY_WHITE);
2335     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2336     struct TextPosInfo *pos = gpc->pos;
2337
2338     // check if panel position is undefined for a certain EMC key or white key
2339     if (gpc->value != EL_EMPTY && pos->x == -1 && pos->y == -1)
2340     {
2341       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2342
2343       // 1st try: display key at the same position as normal or EM keys
2344       if (game_panel_controls[nr_new].value == EL_EMPTY)
2345       {
2346         game_panel_controls[nr_new].value = element;
2347       }
2348       else
2349       {
2350         // 2nd try: display key at the next free position in the key panel
2351         for (k = 0; k < STD_NUM_KEYS; k++)
2352         {
2353           nr_new = GAME_PANEL_KEY_1 + k;
2354
2355           if (game_panel_controls[nr_new].value == EL_EMPTY)
2356           {
2357             game_panel_controls[nr_new].value = element;
2358
2359             break;
2360           }
2361         }
2362       }
2363     }
2364   }
2365
2366   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2367   {
2368     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2369       get_inventory_element_from_pos(local_player, i);
2370     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2371       get_inventory_element_from_pos(local_player, -i - 1);
2372   }
2373
2374   game_panel_controls[GAME_PANEL_SCORE].value = score;
2375   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2376
2377   game_panel_controls[GAME_PANEL_TIME].value = time;
2378
2379   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2380   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2381   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2382
2383   if (level.time == 0)
2384     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2385   else
2386     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2387
2388   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2389   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2390
2391   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2392
2393   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2394     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2395      EL_EMPTY);
2396   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2397     local_player->shield_normal_time_left;
2398   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2399     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2400      EL_EMPTY);
2401   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2402     local_player->shield_deadly_time_left;
2403
2404   game_panel_controls[GAME_PANEL_EXIT].value =
2405     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2406
2407   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2408     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2409   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2410     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2411      EL_EMC_MAGIC_BALL_SWITCH);
2412
2413   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2414     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2415   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2416     game.light_time_left;
2417
2418   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2419     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2420   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2421     game.timegate_time_left;
2422
2423   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2424     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2425
2426   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2427     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2428   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2429     game.lenses_time_left;
2430
2431   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2432     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2433   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2434     game.magnify_time_left;
2435
2436   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2437     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2438      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2439      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2440      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2441      EL_BALLOON_SWITCH_NONE);
2442
2443   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2444     local_player->dynabomb_count;
2445   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2446     local_player->dynabomb_size;
2447   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2448     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2449
2450   game_panel_controls[GAME_PANEL_PENGUINS].value =
2451     game.friends_still_needed;
2452
2453   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2454     game.sokoban_objects_still_needed;
2455   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2456     game.sokoban_fields_still_needed;
2457
2458   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2459     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2460
2461   for (i = 0; i < NUM_BELTS; i++)
2462   {
2463     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2464       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2465        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2466     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2467       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2468   }
2469
2470   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2471     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2472   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2473     game.magic_wall_time_left;
2474
2475   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2476     local_player->gravity;
2477
2478   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2479     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2480
2481   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2482     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2483       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2484        game.panel.element[i].id : EL_UNDEFINED);
2485
2486   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2487     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2488       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2489        element_info[game.panel.element_count[i].id].element_count : 0);
2490
2491   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2492     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2493       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2494        element_info[game.panel.ce_score[i].id].collect_score : 0);
2495
2496   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2497     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2498       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2499        element_info[game.panel.ce_score_element[i].id].collect_score :
2500        EL_UNDEFINED);
2501
2502   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2503   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2504   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2505
2506   // update game panel control frames
2507
2508   for (i = 0; game_panel_controls[i].nr != -1; i++)
2509   {
2510     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2511
2512     if (gpc->type == TYPE_ELEMENT)
2513     {
2514       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2515       {
2516         int last_anim_random_frame = gfx.anim_random_frame;
2517         int element = gpc->value;
2518         int graphic = el2panelimg(element);
2519         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2520                                sync_random_frame : INIT_GFX_RANDOM());
2521
2522         if (gpc->value != gpc->last_value)
2523         {
2524           gpc->gfx_frame = 0;
2525           gpc->gfx_random = init_gfx_random;
2526         }
2527         else
2528         {
2529           gpc->gfx_frame++;
2530
2531           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2532               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2533             gpc->gfx_random = init_gfx_random;
2534         }
2535
2536         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2537           gfx.anim_random_frame = gpc->gfx_random;
2538
2539         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2540           gpc->gfx_frame = element_info[element].collect_score;
2541
2542         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2543
2544         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2545           gfx.anim_random_frame = last_anim_random_frame;
2546       }
2547     }
2548     else if (gpc->type == TYPE_GRAPHIC)
2549     {
2550       if (gpc->graphic != IMG_UNDEFINED)
2551       {
2552         int last_anim_random_frame = gfx.anim_random_frame;
2553         int graphic = gpc->graphic;
2554         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2555                                sync_random_frame : INIT_GFX_RANDOM());
2556
2557         if (gpc->value != gpc->last_value)
2558         {
2559           gpc->gfx_frame = 0;
2560           gpc->gfx_random = init_gfx_random;
2561         }
2562         else
2563         {
2564           gpc->gfx_frame++;
2565
2566           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2567               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2568             gpc->gfx_random = init_gfx_random;
2569         }
2570
2571         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2572           gfx.anim_random_frame = gpc->gfx_random;
2573
2574         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2575
2576         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2577           gfx.anim_random_frame = last_anim_random_frame;
2578       }
2579     }
2580   }
2581 }
2582
2583 static void DisplayGameControlValues(void)
2584 {
2585   boolean redraw_panel = FALSE;
2586   int i;
2587
2588   for (i = 0; game_panel_controls[i].nr != -1; i++)
2589   {
2590     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2591
2592     if (PANEL_DEACTIVATED(gpc->pos))
2593       continue;
2594
2595     if (gpc->value == gpc->last_value &&
2596         gpc->frame == gpc->last_frame)
2597       continue;
2598
2599     redraw_panel = TRUE;
2600   }
2601
2602   if (!redraw_panel)
2603     return;
2604
2605   // copy default game door content to main double buffer
2606
2607   // !!! CHECK AGAIN !!!
2608   SetPanelBackground();
2609   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2610   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2611
2612   // redraw game control buttons
2613   RedrawGameButtons();
2614
2615   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2616
2617   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2618   {
2619     int nr = game_panel_order[i].nr;
2620     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2621     struct TextPosInfo *pos = gpc->pos;
2622     int type = gpc->type;
2623     int value = gpc->value;
2624     int frame = gpc->frame;
2625     int size = pos->size;
2626     int font = pos->font;
2627     boolean draw_masked = pos->draw_masked;
2628     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2629
2630     if (PANEL_DEACTIVATED(pos))
2631       continue;
2632
2633     if (pos->class == get_hash_from_key("extra_panel_items") &&
2634         !setup.prefer_extra_panel_items)
2635       continue;
2636
2637     gpc->last_value = value;
2638     gpc->last_frame = frame;
2639
2640     if (type == TYPE_INTEGER)
2641     {
2642       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2643           nr == GAME_PANEL_TIME)
2644       {
2645         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2646
2647         if (use_dynamic_size)           // use dynamic number of digits
2648         {
2649           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2650           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2651           int size2 = size1 + 1;
2652           int font1 = pos->font;
2653           int font2 = pos->font_alt;
2654
2655           size = (value < value_change ? size1 : size2);
2656           font = (value < value_change ? font1 : font2);
2657         }
2658       }
2659
2660       // correct text size if "digits" is zero or less
2661       if (size <= 0)
2662         size = strlen(int2str(value, size));
2663
2664       // dynamically correct text alignment
2665       pos->width = size * getFontWidth(font);
2666
2667       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2668                   int2str(value, size), font, mask_mode);
2669     }
2670     else if (type == TYPE_ELEMENT)
2671     {
2672       int element, graphic;
2673       Bitmap *src_bitmap;
2674       int src_x, src_y;
2675       int width, height;
2676       int dst_x = PANEL_XPOS(pos);
2677       int dst_y = PANEL_YPOS(pos);
2678
2679       if (value != EL_UNDEFINED && value != EL_EMPTY)
2680       {
2681         element = value;
2682         graphic = el2panelimg(value);
2683
2684 #if 0
2685         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2686               element, EL_NAME(element), size);
2687 #endif
2688
2689         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2690           size = TILESIZE;
2691
2692         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2693                               &src_x, &src_y);
2694
2695         width  = graphic_info[graphic].width  * size / TILESIZE;
2696         height = graphic_info[graphic].height * size / TILESIZE;
2697
2698         if (draw_masked)
2699           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2700                            dst_x, dst_y);
2701         else
2702           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2703                      dst_x, dst_y);
2704       }
2705     }
2706     else if (type == TYPE_GRAPHIC)
2707     {
2708       int graphic        = gpc->graphic;
2709       int graphic_active = gpc->graphic_active;
2710       Bitmap *src_bitmap;
2711       int src_x, src_y;
2712       int width, height;
2713       int dst_x = PANEL_XPOS(pos);
2714       int dst_y = PANEL_YPOS(pos);
2715       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2716                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2717
2718       if (graphic != IMG_UNDEFINED && !skip)
2719       {
2720         if (pos->style == STYLE_REVERSE)
2721           value = 100 - value;
2722
2723         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2724
2725         if (pos->direction & MV_HORIZONTAL)
2726         {
2727           width  = graphic_info[graphic_active].width * value / 100;
2728           height = graphic_info[graphic_active].height;
2729
2730           if (pos->direction == MV_LEFT)
2731           {
2732             src_x += graphic_info[graphic_active].width - width;
2733             dst_x += graphic_info[graphic_active].width - width;
2734           }
2735         }
2736         else
2737         {
2738           width  = graphic_info[graphic_active].width;
2739           height = graphic_info[graphic_active].height * value / 100;
2740
2741           if (pos->direction == MV_UP)
2742           {
2743             src_y += graphic_info[graphic_active].height - height;
2744             dst_y += graphic_info[graphic_active].height - height;
2745           }
2746         }
2747
2748         if (draw_masked)
2749           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2750                            dst_x, dst_y);
2751         else
2752           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2753                      dst_x, dst_y);
2754
2755         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2756
2757         if (pos->direction & MV_HORIZONTAL)
2758         {
2759           if (pos->direction == MV_RIGHT)
2760           {
2761             src_x += width;
2762             dst_x += width;
2763           }
2764           else
2765           {
2766             dst_x = PANEL_XPOS(pos);
2767           }
2768
2769           width = graphic_info[graphic].width - width;
2770         }
2771         else
2772         {
2773           if (pos->direction == MV_DOWN)
2774           {
2775             src_y += height;
2776             dst_y += height;
2777           }
2778           else
2779           {
2780             dst_y = PANEL_YPOS(pos);
2781           }
2782
2783           height = graphic_info[graphic].height - height;
2784         }
2785
2786         if (draw_masked)
2787           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2788                            dst_x, dst_y);
2789         else
2790           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2791                      dst_x, dst_y);
2792       }
2793     }
2794     else if (type == TYPE_STRING)
2795     {
2796       boolean active = (value != 0);
2797       char *state_normal = "off";
2798       char *state_active = "on";
2799       char *state = (active ? state_active : state_normal);
2800       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2801                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2802                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2803                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2804
2805       if (nr == GAME_PANEL_GRAVITY_STATE)
2806       {
2807         int font1 = pos->font;          // (used for normal state)
2808         int font2 = pos->font_alt;      // (used for active state)
2809
2810         font = (active ? font2 : font1);
2811       }
2812
2813       if (s != NULL)
2814       {
2815         char *s_cut;
2816
2817         if (size <= 0)
2818         {
2819           // don't truncate output if "chars" is zero or less
2820           size = strlen(s);
2821
2822           // dynamically correct text alignment
2823           pos->width = size * getFontWidth(font);
2824         }
2825
2826         s_cut = getStringCopyN(s, size);
2827
2828         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2829                     s_cut, font, mask_mode);
2830
2831         free(s_cut);
2832       }
2833     }
2834
2835     redraw_mask |= REDRAW_DOOR_1;
2836   }
2837
2838   SetGameStatus(GAME_MODE_PLAYING);
2839 }
2840
2841 void UpdateAndDisplayGameControlValues(void)
2842 {
2843   if (tape.deactivate_display)
2844     return;
2845
2846   UpdateGameControlValues();
2847   DisplayGameControlValues();
2848 }
2849
2850 #if 0
2851 static void UpdateGameDoorValues(void)
2852 {
2853   UpdateGameControlValues();
2854 }
2855 #endif
2856
2857 void DrawGameDoorValues(void)
2858 {
2859   DisplayGameControlValues();
2860 }
2861
2862
2863 // ============================================================================
2864 // InitGameEngine()
2865 // ----------------------------------------------------------------------------
2866 // initialize game engine due to level / tape version number
2867 // ============================================================================
2868
2869 static void InitGameEngine(void)
2870 {
2871   int i, j, k, l, x, y;
2872
2873   // set game engine from tape file when re-playing, else from level file
2874   game.engine_version = (tape.playing ? tape.engine_version :
2875                          level.game_version);
2876
2877   // set single or multi-player game mode (needed for re-playing tapes)
2878   game.team_mode = setup.team_mode;
2879
2880   if (tape.playing)
2881   {
2882     int num_players = 0;
2883
2884     for (i = 0; i < MAX_PLAYERS; i++)
2885       if (tape.player_participates[i])
2886         num_players++;
2887
2888     // multi-player tapes contain input data for more than one player
2889     game.team_mode = (num_players > 1);
2890   }
2891
2892 #if 0
2893   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2894         level.game_version);
2895   Debug("game:init:level", "          tape.file_version   == %06d",
2896         tape.file_version);
2897   Debug("game:init:level", "          tape.game_version   == %06d",
2898         tape.game_version);
2899   Debug("game:init:level", "          tape.engine_version == %06d",
2900         tape.engine_version);
2901   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2902         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2903 #endif
2904
2905   // --------------------------------------------------------------------------
2906   // set flags for bugs and changes according to active game engine version
2907   // --------------------------------------------------------------------------
2908
2909   /*
2910     Summary of bugfix:
2911     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2912
2913     Bug was introduced in version:
2914     2.0.1
2915
2916     Bug was fixed in version:
2917     4.2.0.0
2918
2919     Description:
2920     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2921     but the property "can fall" was missing, which caused some levels to be
2922     unsolvable. This was fixed in version 4.2.0.0.
2923
2924     Affected levels/tapes:
2925     An example for a tape that was fixed by this bugfix is tape 029 from the
2926     level set "rnd_sam_bateman".
2927     The wrong behaviour will still be used for all levels or tapes that were
2928     created/recorded with it. An example for this is tape 023 from the level
2929     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2930   */
2931
2932   boolean use_amoeba_dropping_cannot_fall_bug =
2933     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2934       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2935      (tape.playing &&
2936       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2937       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2938
2939   /*
2940     Summary of bugfix/change:
2941     Fixed move speed of elements entering or leaving magic wall.
2942
2943     Fixed/changed in version:
2944     2.0.1
2945
2946     Description:
2947     Before 2.0.1, move speed of elements entering or leaving magic wall was
2948     twice as fast as it is now.
2949     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2950
2951     Affected levels/tapes:
2952     The first condition is generally needed for all levels/tapes before version
2953     2.0.1, which might use the old behaviour before it was changed; known tapes
2954     that are affected: Tape 014 from the level set "rnd_conor_mancone".
2955     The second condition is an exception from the above case and is needed for
2956     the special case of tapes recorded with game (not engine!) version 2.0.1 or
2957     above, but before it was known that this change would break tapes like the
2958     above and was fixed in 4.2.0.0, so that the changed behaviour was active
2959     although the engine version while recording maybe was before 2.0.1. There
2960     are a lot of tapes that are affected by this exception, like tape 006 from
2961     the level set "rnd_conor_mancone".
2962   */
2963
2964   boolean use_old_move_stepsize_for_magic_wall =
2965     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
2966      !(tape.playing &&
2967        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2968        tape.game_version <  VERSION_IDENT(4,2,0,0)));
2969
2970   /*
2971     Summary of bugfix/change:
2972     Fixed handling for custom elements that change when pushed by the player.
2973
2974     Fixed/changed in version:
2975     3.1.0
2976
2977     Description:
2978     Before 3.1.0, custom elements that "change when pushing" changed directly
2979     after the player started pushing them (until then handled in "DigField()").
2980     Since 3.1.0, these custom elements are not changed until the "pushing"
2981     move of the element is finished (now handled in "ContinueMoving()").
2982
2983     Affected levels/tapes:
2984     The first condition is generally needed for all levels/tapes before version
2985     3.1.0, which might use the old behaviour before it was changed; known tapes
2986     that are affected are some tapes from the level set "Walpurgis Gardens" by
2987     Jamie Cullen.
2988     The second condition is an exception from the above case and is needed for
2989     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2990     above (including some development versions of 3.1.0), but before it was
2991     known that this change would break tapes like the above and was fixed in
2992     3.1.1, so that the changed behaviour was active although the engine version
2993     while recording maybe was before 3.1.0. There is at least one tape that is
2994     affected by this exception, which is the tape for the one-level set "Bug
2995     Machine" by Juergen Bonhagen.
2996   */
2997
2998   game.use_change_when_pushing_bug =
2999     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3000      !(tape.playing &&
3001        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3002        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3003
3004   /*
3005     Summary of bugfix/change:
3006     Fixed handling for blocking the field the player leaves when moving.
3007
3008     Fixed/changed in version:
3009     3.1.1
3010
3011     Description:
3012     Before 3.1.1, when "block last field when moving" was enabled, the field
3013     the player is leaving when moving was blocked for the time of the move,
3014     and was directly unblocked afterwards. This resulted in the last field
3015     being blocked for exactly one less than the number of frames of one player
3016     move. Additionally, even when blocking was disabled, the last field was
3017     blocked for exactly one frame.
3018     Since 3.1.1, due to changes in player movement handling, the last field
3019     is not blocked at all when blocking is disabled. When blocking is enabled,
3020     the last field is blocked for exactly the number of frames of one player
3021     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3022     last field is blocked for exactly one more than the number of frames of
3023     one player move.
3024
3025     Affected levels/tapes:
3026     (!!! yet to be determined -- probably many !!!)
3027   */
3028
3029   game.use_block_last_field_bug =
3030     (game.engine_version < VERSION_IDENT(3,1,1,0));
3031
3032   /* various special flags and settings for native Emerald Mine game engine */
3033
3034   game_em.use_single_button =
3035     (game.engine_version > VERSION_IDENT(4,0,0,2));
3036
3037   game_em.use_snap_key_bug =
3038     (game.engine_version < VERSION_IDENT(4,0,1,0));
3039
3040   game_em.use_random_bug =
3041     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3042
3043   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3044
3045   game_em.use_old_explosions            = use_old_em_engine;
3046   game_em.use_old_android               = use_old_em_engine;
3047   game_em.use_old_push_elements         = use_old_em_engine;
3048   game_em.use_old_push_into_acid        = use_old_em_engine;
3049
3050   game_em.use_wrap_around               = !use_old_em_engine;
3051
3052   // --------------------------------------------------------------------------
3053
3054   // set maximal allowed number of custom element changes per game frame
3055   game.max_num_changes_per_frame = 1;
3056
3057   // default scan direction: scan playfield from top/left to bottom/right
3058   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3059
3060   // dynamically adjust element properties according to game engine version
3061   InitElementPropertiesEngine(game.engine_version);
3062
3063   // ---------- initialize special element properties -------------------------
3064
3065   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3066   if (use_amoeba_dropping_cannot_fall_bug)
3067     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3068
3069   // ---------- initialize player's initial move delay ------------------------
3070
3071   // dynamically adjust player properties according to level information
3072   for (i = 0; i < MAX_PLAYERS; i++)
3073     game.initial_move_delay_value[i] =
3074       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3075
3076   // dynamically adjust player properties according to game engine version
3077   for (i = 0; i < MAX_PLAYERS; i++)
3078     game.initial_move_delay[i] =
3079       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3080        game.initial_move_delay_value[i] : 0);
3081
3082   // ---------- initialize player's initial push delay ------------------------
3083
3084   // dynamically adjust player properties according to game engine version
3085   game.initial_push_delay_value =
3086     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3087
3088   // ---------- initialize changing elements ----------------------------------
3089
3090   // initialize changing elements information
3091   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3092   {
3093     struct ElementInfo *ei = &element_info[i];
3094
3095     // this pointer might have been changed in the level editor
3096     ei->change = &ei->change_page[0];
3097
3098     if (!IS_CUSTOM_ELEMENT(i))
3099     {
3100       ei->change->target_element = EL_EMPTY_SPACE;
3101       ei->change->delay_fixed = 0;
3102       ei->change->delay_random = 0;
3103       ei->change->delay_frames = 1;
3104     }
3105
3106     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3107     {
3108       ei->has_change_event[j] = FALSE;
3109
3110       ei->event_page_nr[j] = 0;
3111       ei->event_page[j] = &ei->change_page[0];
3112     }
3113   }
3114
3115   // add changing elements from pre-defined list
3116   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3117   {
3118     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3119     struct ElementInfo *ei = &element_info[ch_delay->element];
3120
3121     ei->change->target_element       = ch_delay->target_element;
3122     ei->change->delay_fixed          = ch_delay->change_delay;
3123
3124     ei->change->pre_change_function  = ch_delay->pre_change_function;
3125     ei->change->change_function      = ch_delay->change_function;
3126     ei->change->post_change_function = ch_delay->post_change_function;
3127
3128     ei->change->can_change = TRUE;
3129     ei->change->can_change_or_has_action = TRUE;
3130
3131     ei->has_change_event[CE_DELAY] = TRUE;
3132
3133     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3134     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3135   }
3136
3137   // ---------- initialize internal run-time variables ------------------------
3138
3139   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3140   {
3141     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3142
3143     for (j = 0; j < ei->num_change_pages; j++)
3144     {
3145       ei->change_page[j].can_change_or_has_action =
3146         (ei->change_page[j].can_change |
3147          ei->change_page[j].has_action);
3148     }
3149   }
3150
3151   // add change events from custom element configuration
3152   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3153   {
3154     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3155
3156     for (j = 0; j < ei->num_change_pages; j++)
3157     {
3158       if (!ei->change_page[j].can_change_or_has_action)
3159         continue;
3160
3161       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3162       {
3163         // only add event page for the first page found with this event
3164         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3165         {
3166           ei->has_change_event[k] = TRUE;
3167
3168           ei->event_page_nr[k] = j;
3169           ei->event_page[k] = &ei->change_page[j];
3170         }
3171       }
3172     }
3173   }
3174
3175   // ---------- initialize reference elements in change conditions ------------
3176
3177   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3178   {
3179     int element = EL_CUSTOM_START + i;
3180     struct ElementInfo *ei = &element_info[element];
3181
3182     for (j = 0; j < ei->num_change_pages; j++)
3183     {
3184       int trigger_element = ei->change_page[j].initial_trigger_element;
3185
3186       if (trigger_element >= EL_PREV_CE_8 &&
3187           trigger_element <= EL_NEXT_CE_8)
3188         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3189
3190       ei->change_page[j].trigger_element = trigger_element;
3191     }
3192   }
3193
3194   // ---------- initialize run-time trigger player and element ----------------
3195
3196   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3197   {
3198     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3199
3200     for (j = 0; j < ei->num_change_pages; j++)
3201     {
3202       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3203       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3204       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3205       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3206       ei->change_page[j].actual_trigger_ce_value = 0;
3207       ei->change_page[j].actual_trigger_ce_score = 0;
3208     }
3209   }
3210
3211   // ---------- initialize trigger events -------------------------------------
3212
3213   // initialize trigger events information
3214   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3215     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3216       trigger_events[i][j] = FALSE;
3217
3218   // add trigger events from element change event properties
3219   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3220   {
3221     struct ElementInfo *ei = &element_info[i];
3222
3223     for (j = 0; j < ei->num_change_pages; j++)
3224     {
3225       if (!ei->change_page[j].can_change_or_has_action)
3226         continue;
3227
3228       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3229       {
3230         int trigger_element = ei->change_page[j].trigger_element;
3231
3232         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3233         {
3234           if (ei->change_page[j].has_event[k])
3235           {
3236             if (IS_GROUP_ELEMENT(trigger_element))
3237             {
3238               struct ElementGroupInfo *group =
3239                 element_info[trigger_element].group;
3240
3241               for (l = 0; l < group->num_elements_resolved; l++)
3242                 trigger_events[group->element_resolved[l]][k] = TRUE;
3243             }
3244             else if (trigger_element == EL_ANY_ELEMENT)
3245               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3246                 trigger_events[l][k] = TRUE;
3247             else
3248               trigger_events[trigger_element][k] = TRUE;
3249           }
3250         }
3251       }
3252     }
3253   }
3254
3255   // ---------- initialize push delay -----------------------------------------
3256
3257   // initialize push delay values to default
3258   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3259   {
3260     if (!IS_CUSTOM_ELEMENT(i))
3261     {
3262       // set default push delay values (corrected since version 3.0.7-1)
3263       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3264       {
3265         element_info[i].push_delay_fixed = 2;
3266         element_info[i].push_delay_random = 8;
3267       }
3268       else
3269       {
3270         element_info[i].push_delay_fixed = 8;
3271         element_info[i].push_delay_random = 8;
3272       }
3273     }
3274   }
3275
3276   // set push delay value for certain elements from pre-defined list
3277   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3278   {
3279     int e = push_delay_list[i].element;
3280
3281     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3282     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3283   }
3284
3285   // set push delay value for Supaplex elements for newer engine versions
3286   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3287   {
3288     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3289     {
3290       if (IS_SP_ELEMENT(i))
3291       {
3292         // set SP push delay to just enough to push under a falling zonk
3293         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3294
3295         element_info[i].push_delay_fixed  = delay;
3296         element_info[i].push_delay_random = 0;
3297       }
3298     }
3299   }
3300
3301   // ---------- initialize move stepsize --------------------------------------
3302
3303   // initialize move stepsize values to default
3304   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3305     if (!IS_CUSTOM_ELEMENT(i))
3306       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3307
3308   // set move stepsize value for certain elements from pre-defined list
3309   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3310   {
3311     int e = move_stepsize_list[i].element;
3312
3313     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3314
3315     // set move stepsize value for certain elements for older engine versions
3316     if (use_old_move_stepsize_for_magic_wall)
3317     {
3318       if (e == EL_MAGIC_WALL_FILLING ||
3319           e == EL_MAGIC_WALL_EMPTYING ||
3320           e == EL_BD_MAGIC_WALL_FILLING ||
3321           e == EL_BD_MAGIC_WALL_EMPTYING)
3322         element_info[e].move_stepsize *= 2;
3323     }
3324   }
3325
3326   // ---------- initialize collect score --------------------------------------
3327
3328   // initialize collect score values for custom elements from initial value
3329   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3330     if (IS_CUSTOM_ELEMENT(i))
3331       element_info[i].collect_score = element_info[i].collect_score_initial;
3332
3333   // ---------- initialize collect count --------------------------------------
3334
3335   // initialize collect count values for non-custom elements
3336   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3337     if (!IS_CUSTOM_ELEMENT(i))
3338       element_info[i].collect_count_initial = 0;
3339
3340   // add collect count values for all elements from pre-defined list
3341   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3342     element_info[collect_count_list[i].element].collect_count_initial =
3343       collect_count_list[i].count;
3344
3345   // ---------- initialize access direction -----------------------------------
3346
3347   // initialize access direction values to default (access from every side)
3348   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3349     if (!IS_CUSTOM_ELEMENT(i))
3350       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3351
3352   // set access direction value for certain elements from pre-defined list
3353   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3354     element_info[access_direction_list[i].element].access_direction =
3355       access_direction_list[i].direction;
3356
3357   // ---------- initialize explosion content ----------------------------------
3358   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3359   {
3360     if (IS_CUSTOM_ELEMENT(i))
3361       continue;
3362
3363     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3364     {
3365       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3366
3367       element_info[i].content.e[x][y] =
3368         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3369          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3370          i == EL_PLAYER_3 ? EL_EMERALD :
3371          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3372          i == EL_MOLE ? EL_EMERALD_RED :
3373          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3374          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3375          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3376          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3377          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3378          i == EL_WALL_EMERALD ? EL_EMERALD :
3379          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3380          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3381          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3382          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3383          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3384          i == EL_WALL_PEARL ? EL_PEARL :
3385          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3386          EL_EMPTY);
3387     }
3388   }
3389
3390   // ---------- initialize recursion detection --------------------------------
3391   recursion_loop_depth = 0;
3392   recursion_loop_detected = FALSE;
3393   recursion_loop_element = EL_UNDEFINED;
3394
3395   // ---------- initialize graphics engine ------------------------------------
3396   game.scroll_delay_value =
3397     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3398      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3399      !setup.forced_scroll_delay           ? 0 :
3400      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3401   game.scroll_delay_value =
3402     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3403
3404   // ---------- initialize game engine snapshots ------------------------------
3405   for (i = 0; i < MAX_PLAYERS; i++)
3406     game.snapshot.last_action[i] = 0;
3407   game.snapshot.changed_action = FALSE;
3408   game.snapshot.collected_item = FALSE;
3409   game.snapshot.mode =
3410     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3411      SNAPSHOT_MODE_EVERY_STEP :
3412      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3413      SNAPSHOT_MODE_EVERY_MOVE :
3414      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3415      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3416   game.snapshot.save_snapshot = FALSE;
3417
3418   // ---------- initialize level time for Supaplex engine ---------------------
3419   // Supaplex levels with time limit currently unsupported -- should be added
3420   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3421     level.time = 0;
3422
3423   // ---------- initialize flags for handling game actions --------------------
3424
3425   // set flags for game actions to default values
3426   game.use_key_actions = TRUE;
3427   game.use_mouse_actions = FALSE;
3428
3429   // when using Mirror Magic game engine, handle mouse events only
3430   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3431   {
3432     game.use_key_actions = FALSE;
3433     game.use_mouse_actions = TRUE;
3434   }
3435
3436   // check for custom elements with mouse click events
3437   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3438   {
3439     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3440     {
3441       int element = EL_CUSTOM_START + i;
3442
3443       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3444           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3445           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3446           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3447         game.use_mouse_actions = TRUE;
3448     }
3449   }
3450 }
3451
3452 static int get_num_special_action(int element, int action_first,
3453                                   int action_last)
3454 {
3455   int num_special_action = 0;
3456   int i, j;
3457
3458   for (i = action_first; i <= action_last; i++)
3459   {
3460     boolean found = FALSE;
3461
3462     for (j = 0; j < NUM_DIRECTIONS; j++)
3463       if (el_act_dir2img(element, i, j) !=
3464           el_act_dir2img(element, ACTION_DEFAULT, j))
3465         found = TRUE;
3466
3467     if (found)
3468       num_special_action++;
3469     else
3470       break;
3471   }
3472
3473   return num_special_action;
3474 }
3475
3476
3477 // ============================================================================
3478 // InitGame()
3479 // ----------------------------------------------------------------------------
3480 // initialize and start new game
3481 // ============================================================================
3482
3483 #if DEBUG_INIT_PLAYER
3484 static void DebugPrintPlayerStatus(char *message)
3485 {
3486   int i;
3487
3488   if (!options.debug)
3489     return;
3490
3491   Debug("game:init:player", "%s:", message);
3492
3493   for (i = 0; i < MAX_PLAYERS; i++)
3494   {
3495     struct PlayerInfo *player = &stored_player[i];
3496
3497     Debug("game:init:player",
3498           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3499           i + 1,
3500           player->present,
3501           player->connected,
3502           player->connected_locally,
3503           player->connected_network,
3504           player->active,
3505           (local_player == player ? " (local player)" : ""));
3506   }
3507 }
3508 #endif
3509
3510 void InitGame(void)
3511 {
3512   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3513   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3514   int fade_mask = REDRAW_FIELD;
3515
3516   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3517   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3518   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3519   int initial_move_dir = MV_DOWN;
3520   int i, j, x, y;
3521
3522   // required here to update video display before fading (FIX THIS)
3523   DrawMaskedBorder(REDRAW_DOOR_2);
3524
3525   if (!game.restart_level)
3526     CloseDoor(DOOR_CLOSE_1);
3527
3528   SetGameStatus(GAME_MODE_PLAYING);
3529
3530   if (level_editor_test_game)
3531     FadeSkipNextFadeOut();
3532   else
3533     FadeSetEnterScreen();
3534
3535   if (CheckFadeAll())
3536     fade_mask = REDRAW_ALL;
3537
3538   FadeLevelSoundsAndMusic();
3539
3540   ExpireSoundLoops(TRUE);
3541
3542   FadeOut(fade_mask);
3543
3544   if (level_editor_test_game)
3545     FadeSkipNextFadeIn();
3546
3547   // needed if different viewport properties defined for playing
3548   ChangeViewportPropertiesIfNeeded();
3549
3550   ClearField();
3551
3552   DrawCompleteVideoDisplay();
3553
3554   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3555
3556   InitGameEngine();
3557   InitGameControlValues();
3558
3559   // initialize tape actions from game when recording tape
3560   if (tape.recording)
3561   {
3562     tape.use_key_actions   = game.use_key_actions;
3563     tape.use_mouse_actions = game.use_mouse_actions;
3564   }
3565
3566   // don't play tapes over network
3567   network_playing = (network.enabled && !tape.playing);
3568
3569   for (i = 0; i < MAX_PLAYERS; i++)
3570   {
3571     struct PlayerInfo *player = &stored_player[i];
3572
3573     player->index_nr = i;
3574     player->index_bit = (1 << i);
3575     player->element_nr = EL_PLAYER_1 + i;
3576
3577     player->present = FALSE;
3578     player->active = FALSE;
3579     player->mapped = FALSE;
3580
3581     player->killed = FALSE;
3582     player->reanimated = FALSE;
3583     player->buried = FALSE;
3584
3585     player->action = 0;
3586     player->effective_action = 0;
3587     player->programmed_action = 0;
3588     player->snap_action = 0;
3589
3590     player->mouse_action.lx = 0;
3591     player->mouse_action.ly = 0;
3592     player->mouse_action.button = 0;
3593     player->mouse_action.button_hint = 0;
3594
3595     player->effective_mouse_action.lx = 0;
3596     player->effective_mouse_action.ly = 0;
3597     player->effective_mouse_action.button = 0;
3598     player->effective_mouse_action.button_hint = 0;
3599
3600     for (j = 0; j < MAX_NUM_KEYS; j++)
3601       player->key[j] = FALSE;
3602
3603     player->num_white_keys = 0;
3604
3605     player->dynabomb_count = 0;
3606     player->dynabomb_size = 1;
3607     player->dynabombs_left = 0;
3608     player->dynabomb_xl = FALSE;
3609
3610     player->MovDir = initial_move_dir;
3611     player->MovPos = 0;
3612     player->GfxPos = 0;
3613     player->GfxDir = initial_move_dir;
3614     player->GfxAction = ACTION_DEFAULT;
3615     player->Frame = 0;
3616     player->StepFrame = 0;
3617
3618     player->initial_element = player->element_nr;
3619     player->artwork_element =
3620       (level.use_artwork_element[i] ? level.artwork_element[i] :
3621        player->element_nr);
3622     player->use_murphy = FALSE;
3623
3624     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3625     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3626
3627     player->gravity = level.initial_player_gravity[i];
3628
3629     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3630
3631     player->actual_frame_counter = 0;
3632
3633     player->step_counter = 0;
3634
3635     player->last_move_dir = initial_move_dir;
3636
3637     player->is_active = FALSE;
3638
3639     player->is_waiting = FALSE;
3640     player->is_moving = FALSE;
3641     player->is_auto_moving = FALSE;
3642     player->is_digging = FALSE;
3643     player->is_snapping = FALSE;
3644     player->is_collecting = FALSE;
3645     player->is_pushing = FALSE;
3646     player->is_switching = FALSE;
3647     player->is_dropping = FALSE;
3648     player->is_dropping_pressed = FALSE;
3649
3650     player->is_bored = FALSE;
3651     player->is_sleeping = FALSE;
3652
3653     player->was_waiting = TRUE;
3654     player->was_moving = FALSE;
3655     player->was_snapping = FALSE;
3656     player->was_dropping = FALSE;
3657
3658     player->force_dropping = FALSE;
3659
3660     player->frame_counter_bored = -1;
3661     player->frame_counter_sleeping = -1;
3662
3663     player->anim_delay_counter = 0;
3664     player->post_delay_counter = 0;
3665
3666     player->dir_waiting = initial_move_dir;
3667     player->action_waiting = ACTION_DEFAULT;
3668     player->last_action_waiting = ACTION_DEFAULT;
3669     player->special_action_bored = ACTION_DEFAULT;
3670     player->special_action_sleeping = ACTION_DEFAULT;
3671
3672     player->switch_x = -1;
3673     player->switch_y = -1;
3674
3675     player->drop_x = -1;
3676     player->drop_y = -1;
3677
3678     player->show_envelope = 0;
3679
3680     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3681
3682     player->push_delay       = -1;      // initialized when pushing starts
3683     player->push_delay_value = game.initial_push_delay_value;
3684
3685     player->drop_delay = 0;
3686     player->drop_pressed_delay = 0;
3687
3688     player->last_jx = -1;
3689     player->last_jy = -1;
3690     player->jx = -1;
3691     player->jy = -1;
3692
3693     player->shield_normal_time_left = 0;
3694     player->shield_deadly_time_left = 0;
3695
3696     player->inventory_infinite_element = EL_UNDEFINED;
3697     player->inventory_size = 0;
3698
3699     if (level.use_initial_inventory[i])
3700     {
3701       for (j = 0; j < level.initial_inventory_size[i]; j++)
3702       {
3703         int element = level.initial_inventory_content[i][j];
3704         int collect_count = element_info[element].collect_count_initial;
3705         int k;
3706
3707         if (!IS_CUSTOM_ELEMENT(element))
3708           collect_count = 1;
3709
3710         if (collect_count == 0)
3711           player->inventory_infinite_element = element;
3712         else
3713           for (k = 0; k < collect_count; k++)
3714             if (player->inventory_size < MAX_INVENTORY_SIZE)
3715               player->inventory_element[player->inventory_size++] = element;
3716       }
3717     }
3718
3719     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3720     SnapField(player, 0, 0);
3721
3722     map_player_action[i] = i;
3723   }
3724
3725   network_player_action_received = FALSE;
3726
3727   // initial null action
3728   if (network_playing)
3729     SendToServer_MovePlayer(MV_NONE);
3730
3731   FrameCounter = 0;
3732   TimeFrames = 0;
3733   TimePlayed = 0;
3734   TimeLeft = level.time;
3735   TapeTime = 0;
3736
3737   ScreenMovDir = MV_NONE;
3738   ScreenMovPos = 0;
3739   ScreenGfxPos = 0;
3740
3741   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3742
3743   game.robot_wheel_x = -1;
3744   game.robot_wheel_y = -1;
3745
3746   game.exit_x = -1;
3747   game.exit_y = -1;
3748
3749   game.all_players_gone = FALSE;
3750
3751   game.LevelSolved = FALSE;
3752   game.GameOver = FALSE;
3753
3754   game.GamePlayed = !tape.playing;
3755
3756   game.LevelSolved_GameWon = FALSE;
3757   game.LevelSolved_GameEnd = FALSE;
3758   game.LevelSolved_SaveTape = FALSE;
3759   game.LevelSolved_SaveScore = FALSE;
3760
3761   game.LevelSolved_CountingTime = 0;
3762   game.LevelSolved_CountingScore = 0;
3763   game.LevelSolved_CountingHealth = 0;
3764
3765   game.panel.active = TRUE;
3766
3767   game.no_time_limit = (level.time == 0);
3768
3769   game.yamyam_content_nr = 0;
3770   game.robot_wheel_active = FALSE;
3771   game.magic_wall_active = FALSE;
3772   game.magic_wall_time_left = 0;
3773   game.light_time_left = 0;
3774   game.timegate_time_left = 0;
3775   game.switchgate_pos = 0;
3776   game.wind_direction = level.wind_direction_initial;
3777
3778   game.score = 0;
3779   game.score_final = 0;
3780
3781   game.health = MAX_HEALTH;
3782   game.health_final = MAX_HEALTH;
3783
3784   game.gems_still_needed = level.gems_needed;
3785   game.sokoban_fields_still_needed = 0;
3786   game.sokoban_objects_still_needed = 0;
3787   game.lights_still_needed = 0;
3788   game.players_still_needed = 0;
3789   game.friends_still_needed = 0;
3790
3791   game.lenses_time_left = 0;
3792   game.magnify_time_left = 0;
3793
3794   game.ball_active = level.ball_active_initial;
3795   game.ball_content_nr = 0;
3796
3797   game.explosions_delayed = TRUE;
3798
3799   game.envelope_active = FALSE;
3800
3801   for (i = 0; i < NUM_BELTS; i++)
3802   {
3803     game.belt_dir[i] = MV_NONE;
3804     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3805   }
3806
3807   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3808     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3809
3810 #if DEBUG_INIT_PLAYER
3811   DebugPrintPlayerStatus("Player status at level initialization");
3812 #endif
3813
3814   SCAN_PLAYFIELD(x, y)
3815   {
3816     Tile[x][y] = Last[x][y] = level.field[x][y];
3817     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3818     ChangeDelay[x][y] = 0;
3819     ChangePage[x][y] = -1;
3820     CustomValue[x][y] = 0;              // initialized in InitField()
3821     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3822     AmoebaNr[x][y] = 0;
3823     WasJustMoving[x][y] = 0;
3824     WasJustFalling[x][y] = 0;
3825     CheckCollision[x][y] = 0;
3826     CheckImpact[x][y] = 0;
3827     Stop[x][y] = FALSE;
3828     Pushed[x][y] = FALSE;
3829
3830     ChangeCount[x][y] = 0;
3831     ChangeEvent[x][y] = -1;
3832
3833     ExplodePhase[x][y] = 0;
3834     ExplodeDelay[x][y] = 0;
3835     ExplodeField[x][y] = EX_TYPE_NONE;
3836
3837     RunnerVisit[x][y] = 0;
3838     PlayerVisit[x][y] = 0;
3839
3840     GfxFrame[x][y] = 0;
3841     GfxRandom[x][y] = INIT_GFX_RANDOM();
3842     GfxElement[x][y] = EL_UNDEFINED;
3843     GfxAction[x][y] = ACTION_DEFAULT;
3844     GfxDir[x][y] = MV_NONE;
3845     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3846   }
3847
3848   SCAN_PLAYFIELD(x, y)
3849   {
3850     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3851       emulate_bd = FALSE;
3852     if (emulate_sb && !IS_SB_ELEMENT(Tile[x][y]))
3853       emulate_sb = FALSE;
3854     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3855       emulate_sp = FALSE;
3856
3857     InitField(x, y, TRUE);
3858
3859     ResetGfxAnimation(x, y);
3860   }
3861
3862   InitBeltMovement();
3863
3864   for (i = 0; i < MAX_PLAYERS; i++)
3865   {
3866     struct PlayerInfo *player = &stored_player[i];
3867
3868     // set number of special actions for bored and sleeping animation
3869     player->num_special_action_bored =
3870       get_num_special_action(player->artwork_element,
3871                              ACTION_BORING_1, ACTION_BORING_LAST);
3872     player->num_special_action_sleeping =
3873       get_num_special_action(player->artwork_element,
3874                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3875   }
3876
3877   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3878                     emulate_sb ? EMU_SOKOBAN :
3879                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3880
3881   // initialize type of slippery elements
3882   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3883   {
3884     if (!IS_CUSTOM_ELEMENT(i))
3885     {
3886       // default: elements slip down either to the left or right randomly
3887       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3888
3889       // SP style elements prefer to slip down on the left side
3890       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3891         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3892
3893       // BD style elements prefer to slip down on the left side
3894       if (game.emulation == EMU_BOULDERDASH)
3895         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3896     }
3897   }
3898
3899   // initialize explosion and ignition delay
3900   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3901   {
3902     if (!IS_CUSTOM_ELEMENT(i))
3903     {
3904       int num_phase = 8;
3905       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3906                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3907                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3908       int last_phase = (num_phase + 1) * delay;
3909       int half_phase = (num_phase / 2) * delay;
3910
3911       element_info[i].explosion_delay = last_phase - 1;
3912       element_info[i].ignition_delay = half_phase;
3913
3914       if (i == EL_BLACK_ORB)
3915         element_info[i].ignition_delay = 1;
3916     }
3917   }
3918
3919   // correct non-moving belts to start moving left
3920   for (i = 0; i < NUM_BELTS; i++)
3921     if (game.belt_dir[i] == MV_NONE)
3922       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3923
3924 #if USE_NEW_PLAYER_ASSIGNMENTS
3925   // use preferred player also in local single-player mode
3926   if (!network.enabled && !game.team_mode)
3927   {
3928     int new_index_nr = setup.network_player_nr;
3929
3930     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3931     {
3932       for (i = 0; i < MAX_PLAYERS; i++)
3933         stored_player[i].connected_locally = FALSE;
3934
3935       stored_player[new_index_nr].connected_locally = TRUE;
3936     }
3937   }
3938
3939   for (i = 0; i < MAX_PLAYERS; i++)
3940   {
3941     stored_player[i].connected = FALSE;
3942
3943     // in network game mode, the local player might not be the first player
3944     if (stored_player[i].connected_locally)
3945       local_player = &stored_player[i];
3946   }
3947
3948   if (!network.enabled)
3949     local_player->connected = TRUE;
3950
3951   if (tape.playing)
3952   {
3953     for (i = 0; i < MAX_PLAYERS; i++)
3954       stored_player[i].connected = tape.player_participates[i];
3955   }
3956   else if (network.enabled)
3957   {
3958     // add team mode players connected over the network (needed for correct
3959     // assignment of player figures from level to locally playing players)
3960
3961     for (i = 0; i < MAX_PLAYERS; i++)
3962       if (stored_player[i].connected_network)
3963         stored_player[i].connected = TRUE;
3964   }
3965   else if (game.team_mode)
3966   {
3967     // try to guess locally connected team mode players (needed for correct
3968     // assignment of player figures from level to locally playing players)
3969
3970     for (i = 0; i < MAX_PLAYERS; i++)
3971       if (setup.input[i].use_joystick ||
3972           setup.input[i].key.left != KSYM_UNDEFINED)
3973         stored_player[i].connected = TRUE;
3974   }
3975
3976 #if DEBUG_INIT_PLAYER
3977   DebugPrintPlayerStatus("Player status after level initialization");
3978 #endif
3979
3980 #if DEBUG_INIT_PLAYER
3981   Debug("game:init:player", "Reassigning players ...");
3982 #endif
3983
3984   // check if any connected player was not found in playfield
3985   for (i = 0; i < MAX_PLAYERS; i++)
3986   {
3987     struct PlayerInfo *player = &stored_player[i];
3988
3989     if (player->connected && !player->present)
3990     {
3991       struct PlayerInfo *field_player = NULL;
3992
3993 #if DEBUG_INIT_PLAYER
3994       Debug("game:init:player",
3995             "- looking for field player for player %d ...", i + 1);
3996 #endif
3997
3998       // assign first free player found that is present in the playfield
3999
4000       // first try: look for unmapped playfield player that is not connected
4001       for (j = 0; j < MAX_PLAYERS; j++)
4002         if (field_player == NULL &&
4003             stored_player[j].present &&
4004             !stored_player[j].mapped &&
4005             !stored_player[j].connected)
4006           field_player = &stored_player[j];
4007
4008       // second try: look for *any* unmapped playfield player
4009       for (j = 0; j < MAX_PLAYERS; j++)
4010         if (field_player == NULL &&
4011             stored_player[j].present &&
4012             !stored_player[j].mapped)
4013           field_player = &stored_player[j];
4014
4015       if (field_player != NULL)
4016       {
4017         int jx = field_player->jx, jy = field_player->jy;
4018
4019 #if DEBUG_INIT_PLAYER
4020         Debug("game:init:player", "- found player %d",
4021               field_player->index_nr + 1);
4022 #endif
4023
4024         player->present = FALSE;
4025         player->active = FALSE;
4026
4027         field_player->present = TRUE;
4028         field_player->active = TRUE;
4029
4030         /*
4031         player->initial_element = field_player->initial_element;
4032         player->artwork_element = field_player->artwork_element;
4033
4034         player->block_last_field       = field_player->block_last_field;
4035         player->block_delay_adjustment = field_player->block_delay_adjustment;
4036         */
4037
4038         StorePlayer[jx][jy] = field_player->element_nr;
4039
4040         field_player->jx = field_player->last_jx = jx;
4041         field_player->jy = field_player->last_jy = jy;
4042
4043         if (local_player == player)
4044           local_player = field_player;
4045
4046         map_player_action[field_player->index_nr] = i;
4047
4048         field_player->mapped = TRUE;
4049
4050 #if DEBUG_INIT_PLAYER
4051         Debug("game:init:player", "- map_player_action[%d] == %d",
4052               field_player->index_nr + 1, i + 1);
4053 #endif
4054       }
4055     }
4056
4057     if (player->connected && player->present)
4058       player->mapped = TRUE;
4059   }
4060
4061 #if DEBUG_INIT_PLAYER
4062   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4063 #endif
4064
4065 #else
4066
4067   // check if any connected player was not found in playfield
4068   for (i = 0; i < MAX_PLAYERS; i++)
4069   {
4070     struct PlayerInfo *player = &stored_player[i];
4071
4072     if (player->connected && !player->present)
4073     {
4074       for (j = 0; j < MAX_PLAYERS; j++)
4075       {
4076         struct PlayerInfo *field_player = &stored_player[j];
4077         int jx = field_player->jx, jy = field_player->jy;
4078
4079         // assign first free player found that is present in the playfield
4080         if (field_player->present && !field_player->connected)
4081         {
4082           player->present = TRUE;
4083           player->active = TRUE;
4084
4085           field_player->present = FALSE;
4086           field_player->active = FALSE;
4087
4088           player->initial_element = field_player->initial_element;
4089           player->artwork_element = field_player->artwork_element;
4090
4091           player->block_last_field       = field_player->block_last_field;
4092           player->block_delay_adjustment = field_player->block_delay_adjustment;
4093
4094           StorePlayer[jx][jy] = player->element_nr;
4095
4096           player->jx = player->last_jx = jx;
4097           player->jy = player->last_jy = jy;
4098
4099           break;
4100         }
4101       }
4102     }
4103   }
4104 #endif
4105
4106 #if 0
4107   Debug("game:init:player", "local_player->present == %d",
4108         local_player->present);
4109 #endif
4110
4111   // set focus to local player for network games, else to all players
4112   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4113   game.centered_player_nr_next = game.centered_player_nr;
4114   game.set_centered_player = FALSE;
4115   game.set_centered_player_wrap = FALSE;
4116
4117   if (network_playing && tape.recording)
4118   {
4119     // store client dependent player focus when recording network games
4120     tape.centered_player_nr_next = game.centered_player_nr_next;
4121     tape.set_centered_player = TRUE;
4122   }
4123
4124   if (tape.playing)
4125   {
4126     // when playing a tape, eliminate all players who do not participate
4127
4128 #if USE_NEW_PLAYER_ASSIGNMENTS
4129
4130     if (!game.team_mode)
4131     {
4132       for (i = 0; i < MAX_PLAYERS; i++)
4133       {
4134         if (stored_player[i].active &&
4135             !tape.player_participates[map_player_action[i]])
4136         {
4137           struct PlayerInfo *player = &stored_player[i];
4138           int jx = player->jx, jy = player->jy;
4139
4140 #if DEBUG_INIT_PLAYER
4141           Debug("game:init:player", "Removing player %d at (%d, %d)",
4142                 i + 1, jx, jy);
4143 #endif
4144
4145           player->active = FALSE;
4146           StorePlayer[jx][jy] = 0;
4147           Tile[jx][jy] = EL_EMPTY;
4148         }
4149       }
4150     }
4151
4152 #else
4153
4154     for (i = 0; i < MAX_PLAYERS; i++)
4155     {
4156       if (stored_player[i].active &&
4157           !tape.player_participates[i])
4158       {
4159         struct PlayerInfo *player = &stored_player[i];
4160         int jx = player->jx, jy = player->jy;
4161
4162         player->active = FALSE;
4163         StorePlayer[jx][jy] = 0;
4164         Tile[jx][jy] = EL_EMPTY;
4165       }
4166     }
4167 #endif
4168   }
4169   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4170   {
4171     // when in single player mode, eliminate all but the local player
4172
4173     for (i = 0; i < MAX_PLAYERS; i++)
4174     {
4175       struct PlayerInfo *player = &stored_player[i];
4176
4177       if (player->active && player != local_player)
4178       {
4179         int jx = player->jx, jy = player->jy;
4180
4181         player->active = FALSE;
4182         player->present = FALSE;
4183
4184         StorePlayer[jx][jy] = 0;
4185         Tile[jx][jy] = EL_EMPTY;
4186       }
4187     }
4188   }
4189
4190   for (i = 0; i < MAX_PLAYERS; i++)
4191     if (stored_player[i].active)
4192       game.players_still_needed++;
4193
4194   if (level.solved_by_one_player)
4195     game.players_still_needed = 1;
4196
4197   // when recording the game, store which players take part in the game
4198   if (tape.recording)
4199   {
4200 #if USE_NEW_PLAYER_ASSIGNMENTS
4201     for (i = 0; i < MAX_PLAYERS; i++)
4202       if (stored_player[i].connected)
4203         tape.player_participates[i] = TRUE;
4204 #else
4205     for (i = 0; i < MAX_PLAYERS; i++)
4206       if (stored_player[i].active)
4207         tape.player_participates[i] = TRUE;
4208 #endif
4209   }
4210
4211 #if DEBUG_INIT_PLAYER
4212   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4213 #endif
4214
4215   if (BorderElement == EL_EMPTY)
4216   {
4217     SBX_Left = 0;
4218     SBX_Right = lev_fieldx - SCR_FIELDX;
4219     SBY_Upper = 0;
4220     SBY_Lower = lev_fieldy - SCR_FIELDY;
4221   }
4222   else
4223   {
4224     SBX_Left = -1;
4225     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4226     SBY_Upper = -1;
4227     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4228   }
4229
4230   if (full_lev_fieldx <= SCR_FIELDX)
4231     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4232   if (full_lev_fieldy <= SCR_FIELDY)
4233     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4234
4235   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4236     SBX_Left--;
4237   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4238     SBY_Upper--;
4239
4240   // if local player not found, look for custom element that might create
4241   // the player (make some assumptions about the right custom element)
4242   if (!local_player->present)
4243   {
4244     int start_x = 0, start_y = 0;
4245     int found_rating = 0;
4246     int found_element = EL_UNDEFINED;
4247     int player_nr = local_player->index_nr;
4248
4249     SCAN_PLAYFIELD(x, y)
4250     {
4251       int element = Tile[x][y];
4252       int content;
4253       int xx, yy;
4254       boolean is_player;
4255
4256       if (level.use_start_element[player_nr] &&
4257           level.start_element[player_nr] == element &&
4258           found_rating < 4)
4259       {
4260         start_x = x;
4261         start_y = y;
4262
4263         found_rating = 4;
4264         found_element = element;
4265       }
4266
4267       if (!IS_CUSTOM_ELEMENT(element))
4268         continue;
4269
4270       if (CAN_CHANGE(element))
4271       {
4272         for (i = 0; i < element_info[element].num_change_pages; i++)
4273         {
4274           // check for player created from custom element as single target
4275           content = element_info[element].change_page[i].target_element;
4276           is_player = ELEM_IS_PLAYER(content);
4277
4278           if (is_player && (found_rating < 3 ||
4279                             (found_rating == 3 && element < found_element)))
4280           {
4281             start_x = x;
4282             start_y = y;
4283
4284             found_rating = 3;
4285             found_element = element;
4286           }
4287         }
4288       }
4289
4290       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4291       {
4292         // check for player created from custom element as explosion content
4293         content = element_info[element].content.e[xx][yy];
4294         is_player = ELEM_IS_PLAYER(content);
4295
4296         if (is_player && (found_rating < 2 ||
4297                           (found_rating == 2 && element < found_element)))
4298         {
4299           start_x = x + xx - 1;
4300           start_y = y + yy - 1;
4301
4302           found_rating = 2;
4303           found_element = element;
4304         }
4305
4306         if (!CAN_CHANGE(element))
4307           continue;
4308
4309         for (i = 0; i < element_info[element].num_change_pages; i++)
4310         {
4311           // check for player created from custom element as extended target
4312           content =
4313             element_info[element].change_page[i].target_content.e[xx][yy];
4314
4315           is_player = ELEM_IS_PLAYER(content);
4316
4317           if (is_player && (found_rating < 1 ||
4318                             (found_rating == 1 && element < found_element)))
4319           {
4320             start_x = x + xx - 1;
4321             start_y = y + yy - 1;
4322
4323             found_rating = 1;
4324             found_element = element;
4325           }
4326         }
4327       }
4328     }
4329
4330     scroll_x = SCROLL_POSITION_X(start_x);
4331     scroll_y = SCROLL_POSITION_Y(start_y);
4332   }
4333   else
4334   {
4335     scroll_x = SCROLL_POSITION_X(local_player->jx);
4336     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4337   }
4338
4339   // !!! FIX THIS (START) !!!
4340   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4341   {
4342     InitGameEngine_EM();
4343   }
4344   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4345   {
4346     InitGameEngine_SP();
4347   }
4348   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4349   {
4350     InitGameEngine_MM();
4351   }
4352   else
4353   {
4354     DrawLevel(REDRAW_FIELD);
4355     DrawAllPlayers();
4356
4357     // after drawing the level, correct some elements
4358     if (game.timegate_time_left == 0)
4359       CloseAllOpenTimegates();
4360   }
4361
4362   // blit playfield from scroll buffer to normal back buffer for fading in
4363   BlitScreenToBitmap(backbuffer);
4364   // !!! FIX THIS (END) !!!
4365
4366   DrawMaskedBorder(fade_mask);
4367
4368   FadeIn(fade_mask);
4369
4370 #if 1
4371   // full screen redraw is required at this point in the following cases:
4372   // - special editor door undrawn when game was started from level editor
4373   // - drawing area (playfield) was changed and has to be removed completely
4374   redraw_mask = REDRAW_ALL;
4375   BackToFront();
4376 #endif
4377
4378   if (!game.restart_level)
4379   {
4380     // copy default game door content to main double buffer
4381
4382     // !!! CHECK AGAIN !!!
4383     SetPanelBackground();
4384     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4385     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4386   }
4387
4388   SetPanelBackground();
4389   SetDrawBackgroundMask(REDRAW_DOOR_1);
4390
4391   UpdateAndDisplayGameControlValues();
4392
4393   if (!game.restart_level)
4394   {
4395     UnmapGameButtons();
4396     UnmapTapeButtons();
4397
4398     FreeGameButtons();
4399     CreateGameButtons();
4400
4401     MapGameButtons();
4402     MapTapeButtons();
4403
4404     // copy actual game door content to door double buffer for OpenDoor()
4405     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4406
4407     OpenDoor(DOOR_OPEN_ALL);
4408
4409     KeyboardAutoRepeatOffUnlessAutoplay();
4410
4411 #if DEBUG_INIT_PLAYER
4412     DebugPrintPlayerStatus("Player status (final)");
4413 #endif
4414   }
4415
4416   UnmapAllGadgets();
4417
4418   MapGameButtons();
4419   MapTapeButtons();
4420
4421   if (!game.restart_level && !tape.playing)
4422   {
4423     LevelStats_incPlayed(level_nr);
4424
4425     SaveLevelSetup_SeriesInfo();
4426   }
4427
4428   game.restart_level = FALSE;
4429   game.restart_game_message = NULL;
4430   game.request_active = FALSE;
4431
4432   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4433     InitGameActions_MM();
4434
4435   SaveEngineSnapshotToListInitial();
4436
4437   if (!game.restart_level)
4438   {
4439     PlaySound(SND_GAME_STARTING);
4440
4441     if (setup.sound_music)
4442       PlayLevelMusic();
4443   }
4444 }
4445
4446 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4447                         int actual_player_x, int actual_player_y)
4448 {
4449   // this is used for non-R'n'D game engines to update certain engine values
4450
4451   // needed to determine if sounds are played within the visible screen area
4452   scroll_x = actual_scroll_x;
4453   scroll_y = actual_scroll_y;
4454
4455   // needed to get player position for "follow finger" playing input method
4456   local_player->jx = actual_player_x;
4457   local_player->jy = actual_player_y;
4458 }
4459
4460 void InitMovDir(int x, int y)
4461 {
4462   int i, element = Tile[x][y];
4463   static int xy[4][2] =
4464   {
4465     {  0, +1 },
4466     { +1,  0 },
4467     {  0, -1 },
4468     { -1,  0 }
4469   };
4470   static int direction[3][4] =
4471   {
4472     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4473     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4474     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4475   };
4476
4477   switch (element)
4478   {
4479     case EL_BUG_RIGHT:
4480     case EL_BUG_UP:
4481     case EL_BUG_LEFT:
4482     case EL_BUG_DOWN:
4483       Tile[x][y] = EL_BUG;
4484       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4485       break;
4486
4487     case EL_SPACESHIP_RIGHT:
4488     case EL_SPACESHIP_UP:
4489     case EL_SPACESHIP_LEFT:
4490     case EL_SPACESHIP_DOWN:
4491       Tile[x][y] = EL_SPACESHIP;
4492       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4493       break;
4494
4495     case EL_BD_BUTTERFLY_RIGHT:
4496     case EL_BD_BUTTERFLY_UP:
4497     case EL_BD_BUTTERFLY_LEFT:
4498     case EL_BD_BUTTERFLY_DOWN:
4499       Tile[x][y] = EL_BD_BUTTERFLY;
4500       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4501       break;
4502
4503     case EL_BD_FIREFLY_RIGHT:
4504     case EL_BD_FIREFLY_UP:
4505     case EL_BD_FIREFLY_LEFT:
4506     case EL_BD_FIREFLY_DOWN:
4507       Tile[x][y] = EL_BD_FIREFLY;
4508       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4509       break;
4510
4511     case EL_PACMAN_RIGHT:
4512     case EL_PACMAN_UP:
4513     case EL_PACMAN_LEFT:
4514     case EL_PACMAN_DOWN:
4515       Tile[x][y] = EL_PACMAN;
4516       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4517       break;
4518
4519     case EL_YAMYAM_LEFT:
4520     case EL_YAMYAM_RIGHT:
4521     case EL_YAMYAM_UP:
4522     case EL_YAMYAM_DOWN:
4523       Tile[x][y] = EL_YAMYAM;
4524       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4525       break;
4526
4527     case EL_SP_SNIKSNAK:
4528       MovDir[x][y] = MV_UP;
4529       break;
4530
4531     case EL_SP_ELECTRON:
4532       MovDir[x][y] = MV_LEFT;
4533       break;
4534
4535     case EL_MOLE_LEFT:
4536     case EL_MOLE_RIGHT:
4537     case EL_MOLE_UP:
4538     case EL_MOLE_DOWN:
4539       Tile[x][y] = EL_MOLE;
4540       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4541       break;
4542
4543     case EL_SPRING_LEFT:
4544     case EL_SPRING_RIGHT:
4545       Tile[x][y] = EL_SPRING;
4546       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4547       break;
4548
4549     default:
4550       if (IS_CUSTOM_ELEMENT(element))
4551       {
4552         struct ElementInfo *ei = &element_info[element];
4553         int move_direction_initial = ei->move_direction_initial;
4554         int move_pattern = ei->move_pattern;
4555
4556         if (move_direction_initial == MV_START_PREVIOUS)
4557         {
4558           if (MovDir[x][y] != MV_NONE)
4559             return;
4560
4561           move_direction_initial = MV_START_AUTOMATIC;
4562         }
4563
4564         if (move_direction_initial == MV_START_RANDOM)
4565           MovDir[x][y] = 1 << RND(4);
4566         else if (move_direction_initial & MV_ANY_DIRECTION)
4567           MovDir[x][y] = move_direction_initial;
4568         else if (move_pattern == MV_ALL_DIRECTIONS ||
4569                  move_pattern == MV_TURNING_LEFT ||
4570                  move_pattern == MV_TURNING_RIGHT ||
4571                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4572                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4573                  move_pattern == MV_TURNING_RANDOM)
4574           MovDir[x][y] = 1 << RND(4);
4575         else if (move_pattern == MV_HORIZONTAL)
4576           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4577         else if (move_pattern == MV_VERTICAL)
4578           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4579         else if (move_pattern & MV_ANY_DIRECTION)
4580           MovDir[x][y] = element_info[element].move_pattern;
4581         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4582                  move_pattern == MV_ALONG_RIGHT_SIDE)
4583         {
4584           // use random direction as default start direction
4585           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4586             MovDir[x][y] = 1 << RND(4);
4587
4588           for (i = 0; i < NUM_DIRECTIONS; i++)
4589           {
4590             int x1 = x + xy[i][0];
4591             int y1 = y + xy[i][1];
4592
4593             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4594             {
4595               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4596                 MovDir[x][y] = direction[0][i];
4597               else
4598                 MovDir[x][y] = direction[1][i];
4599
4600               break;
4601             }
4602           }
4603         }                
4604       }
4605       else
4606       {
4607         MovDir[x][y] = 1 << RND(4);
4608
4609         if (element != EL_BUG &&
4610             element != EL_SPACESHIP &&
4611             element != EL_BD_BUTTERFLY &&
4612             element != EL_BD_FIREFLY)
4613           break;
4614
4615         for (i = 0; i < NUM_DIRECTIONS; i++)
4616         {
4617           int x1 = x + xy[i][0];
4618           int y1 = y + xy[i][1];
4619
4620           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4621           {
4622             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4623             {
4624               MovDir[x][y] = direction[0][i];
4625               break;
4626             }
4627             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4628                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4629             {
4630               MovDir[x][y] = direction[1][i];
4631               break;
4632             }
4633           }
4634         }
4635       }
4636       break;
4637   }
4638
4639   GfxDir[x][y] = MovDir[x][y];
4640 }
4641
4642 void InitAmoebaNr(int x, int y)
4643 {
4644   int i;
4645   int group_nr = AmoebaNeighbourNr(x, y);
4646
4647   if (group_nr == 0)
4648   {
4649     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4650     {
4651       if (AmoebaCnt[i] == 0)
4652       {
4653         group_nr = i;
4654         break;
4655       }
4656     }
4657   }
4658
4659   AmoebaNr[x][y] = group_nr;
4660   AmoebaCnt[group_nr]++;
4661   AmoebaCnt2[group_nr]++;
4662 }
4663
4664 static void LevelSolved(void)
4665 {
4666   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4667       game.players_still_needed > 0)
4668     return;
4669
4670   game.LevelSolved = TRUE;
4671   game.GameOver = TRUE;
4672
4673   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4674                       game_em.lev->score :
4675                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4676                       game_mm.score :
4677                       game.score);
4678   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4679                        MM_HEALTH(game_mm.laser_overload_value) :
4680                        game.health);
4681
4682   game.LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed : TimeLeft);
4683   game.LevelSolved_CountingScore = game.score_final;
4684   game.LevelSolved_CountingHealth = game.health_final;
4685 }
4686
4687 void GameWon(void)
4688 {
4689   static int time_count_steps;
4690   static int time, time_final;
4691   static int score, score_final;
4692   static int health, health_final;
4693   static int game_over_delay_1 = 0;
4694   static int game_over_delay_2 = 0;
4695   static int game_over_delay_3 = 0;
4696   int game_over_delay_value_1 = 50;
4697   int game_over_delay_value_2 = 25;
4698   int game_over_delay_value_3 = 50;
4699
4700   if (!game.LevelSolved_GameWon)
4701   {
4702     int i;
4703
4704     // do not start end game actions before the player stops moving (to exit)
4705     if (local_player->active && local_player->MovPos)
4706       return;
4707
4708     game.LevelSolved_GameWon = TRUE;
4709     game.LevelSolved_SaveTape = tape.recording;
4710     game.LevelSolved_SaveScore = !tape.playing;
4711
4712     if (!tape.playing)
4713     {
4714       LevelStats_incSolved(level_nr);
4715
4716       SaveLevelSetup_SeriesInfo();
4717     }
4718
4719     if (tape.auto_play)         // tape might already be stopped here
4720       tape.auto_play_level_solved = TRUE;
4721
4722     TapeStop();
4723
4724     game_over_delay_1 = 0;
4725     game_over_delay_2 = 0;
4726     game_over_delay_3 = game_over_delay_value_3;
4727
4728     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4729     score = score_final = game.score_final;
4730     health = health_final = game.health_final;
4731
4732     if (level.score[SC_TIME_BONUS] > 0)
4733     {
4734       if (TimeLeft > 0)
4735       {
4736         time_final = 0;
4737         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4738       }
4739       else if (game.no_time_limit && TimePlayed < 999)
4740       {
4741         time_final = 999;
4742         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4743       }
4744
4745       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4746
4747       game_over_delay_1 = game_over_delay_value_1;
4748
4749       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4750       {
4751         health_final = 0;
4752         score_final += health * level.score[SC_TIME_BONUS];
4753
4754         game_over_delay_2 = game_over_delay_value_2;
4755       }
4756
4757       game.score_final = score_final;
4758       game.health_final = health_final;
4759     }
4760
4761     if (level_editor_test_game)
4762     {
4763       time = time_final;
4764       score = score_final;
4765
4766       game.LevelSolved_CountingTime = time;
4767       game.LevelSolved_CountingScore = score;
4768
4769       game_panel_controls[GAME_PANEL_TIME].value = time;
4770       game_panel_controls[GAME_PANEL_SCORE].value = score;
4771
4772       DisplayGameControlValues();
4773     }
4774
4775     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4776     {
4777       // check if last player has left the level
4778       if (game.exit_x >= 0 &&
4779           game.exit_y >= 0)
4780       {
4781         int x = game.exit_x;
4782         int y = game.exit_y;
4783         int element = Tile[x][y];
4784
4785         // close exit door after last player
4786         if ((game.all_players_gone &&
4787              (element == EL_EXIT_OPEN ||
4788               element == EL_SP_EXIT_OPEN ||
4789               element == EL_STEEL_EXIT_OPEN)) ||
4790             element == EL_EM_EXIT_OPEN ||
4791             element == EL_EM_STEEL_EXIT_OPEN)
4792         {
4793
4794           Tile[x][y] =
4795             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4796              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4797              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4798              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4799              EL_EM_STEEL_EXIT_CLOSING);
4800
4801           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4802         }
4803
4804         // player disappears
4805         DrawLevelField(x, y);
4806       }
4807
4808       for (i = 0; i < MAX_PLAYERS; i++)
4809       {
4810         struct PlayerInfo *player = &stored_player[i];
4811
4812         if (player->present)
4813         {
4814           RemovePlayer(player);
4815
4816           // player disappears
4817           DrawLevelField(player->jx, player->jy);
4818         }
4819       }
4820     }
4821
4822     PlaySound(SND_GAME_WINNING);
4823   }
4824
4825   if (game_over_delay_1 > 0)
4826   {
4827     game_over_delay_1--;
4828
4829     return;
4830   }
4831
4832   if (time != time_final)
4833   {
4834     int time_to_go = ABS(time_final - time);
4835     int time_count_dir = (time < time_final ? +1 : -1);
4836
4837     if (time_to_go < time_count_steps)
4838       time_count_steps = 1;
4839
4840     time  += time_count_steps * time_count_dir;
4841     score += time_count_steps * level.score[SC_TIME_BONUS];
4842
4843     game.LevelSolved_CountingTime = time;
4844     game.LevelSolved_CountingScore = score;
4845
4846     game_panel_controls[GAME_PANEL_TIME].value = time;
4847     game_panel_controls[GAME_PANEL_SCORE].value = score;
4848
4849     DisplayGameControlValues();
4850
4851     if (time == time_final)
4852       StopSound(SND_GAME_LEVELTIME_BONUS);
4853     else if (setup.sound_loops)
4854       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4855     else
4856       PlaySound(SND_GAME_LEVELTIME_BONUS);
4857
4858     return;
4859   }
4860
4861   if (game_over_delay_2 > 0)
4862   {
4863     game_over_delay_2--;
4864
4865     return;
4866   }
4867
4868   if (health != health_final)
4869   {
4870     int health_count_dir = (health < health_final ? +1 : -1);
4871
4872     health += health_count_dir;
4873     score  += level.score[SC_TIME_BONUS];
4874
4875     game.LevelSolved_CountingHealth = health;
4876     game.LevelSolved_CountingScore = score;
4877
4878     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4879     game_panel_controls[GAME_PANEL_SCORE].value = score;
4880
4881     DisplayGameControlValues();
4882
4883     if (health == health_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   game.panel.active = FALSE;
4894
4895   if (game_over_delay_3 > 0)
4896   {
4897     game_over_delay_3--;
4898
4899     return;
4900   }
4901
4902   GameEnd();
4903 }
4904
4905 void GameEnd(void)
4906 {
4907   // used instead of "level_nr" (needed for network games)
4908   int last_level_nr = levelset.level_nr;
4909   int hi_pos;
4910
4911   game.LevelSolved_GameEnd = TRUE;
4912
4913   if (game.LevelSolved_SaveTape)
4914   {
4915     // make sure that request dialog to save tape does not open door again
4916     if (!global.use_envelope_request)
4917       CloseDoor(DOOR_CLOSE_1);
4918
4919     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4920   }
4921
4922   // if no tape is to be saved, close both doors simultaneously
4923   CloseDoor(DOOR_CLOSE_ALL);
4924
4925   if (level_editor_test_game)
4926   {
4927     SetGameStatus(GAME_MODE_MAIN);
4928
4929     DrawMainMenu();
4930
4931     return;
4932   }
4933
4934   if (!game.LevelSolved_SaveScore)
4935   {
4936     SetGameStatus(GAME_MODE_MAIN);
4937
4938     DrawMainMenu();
4939
4940     return;
4941   }
4942
4943   if (level_nr == leveldir_current->handicap_level)
4944   {
4945     leveldir_current->handicap_level++;
4946
4947     SaveLevelSetup_SeriesInfo();
4948   }
4949
4950   if (setup.increment_levels &&
4951       level_nr < leveldir_current->last_level &&
4952       !network_playing)
4953   {
4954     level_nr++;         // advance to next level
4955     TapeErase();        // start with empty tape
4956
4957     if (setup.auto_play_next_level)
4958     {
4959       LoadLevel(level_nr);
4960
4961       SaveLevelSetup_SeriesInfo();
4962     }
4963   }
4964
4965   hi_pos = NewHiScore(last_level_nr);
4966
4967   if (hi_pos >= 0 && !setup.skip_scores_after_game)
4968   {
4969     SetGameStatus(GAME_MODE_SCORES);
4970
4971     DrawHallOfFame(last_level_nr, hi_pos);
4972   }
4973   else if (setup.auto_play_next_level && setup.increment_levels &&
4974            last_level_nr < leveldir_current->last_level &&
4975            !network_playing)
4976   {
4977     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4978   }
4979   else
4980   {
4981     SetGameStatus(GAME_MODE_MAIN);
4982
4983     DrawMainMenu();
4984   }
4985 }
4986
4987 int NewHiScore(int level_nr)
4988 {
4989   int k, l;
4990   int position = -1;
4991   boolean one_score_entry_per_name = !program.many_scores_per_name;
4992
4993   LoadScore(level_nr);
4994
4995   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4996       game.score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4997     return -1;
4998
4999   for (k = 0; k < MAX_SCORE_ENTRIES; k++)
5000   {
5001     if (game.score_final > highscore[k].Score)
5002     {
5003       // player has made it to the hall of fame
5004
5005       if (k < MAX_SCORE_ENTRIES - 1)
5006       {
5007         int m = MAX_SCORE_ENTRIES - 1;
5008
5009         if (one_score_entry_per_name)
5010         {
5011           for (l = k; l < MAX_SCORE_ENTRIES; l++)
5012             if (strEqual(setup.player_name, highscore[l].Name))
5013               m = l;
5014
5015           if (m == k)   // player's new highscore overwrites his old one
5016             goto put_into_list;
5017         }
5018
5019         for (l = m; l > k; l--)
5020         {
5021           strcpy(highscore[l].Name, highscore[l - 1].Name);
5022           highscore[l].Score = highscore[l - 1].Score;
5023         }
5024       }
5025
5026       put_into_list:
5027
5028       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5029       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5030       highscore[k].Score = game.score_final;
5031       position = k;
5032
5033       break;
5034     }
5035     else if (one_score_entry_per_name &&
5036              !strncmp(setup.player_name, highscore[k].Name,
5037                       MAX_PLAYER_NAME_LEN))
5038       break;    // player already there with a higher score
5039   }
5040
5041   if (position >= 0) 
5042     SaveScore(level_nr);
5043
5044   return position;
5045 }
5046
5047 static int getElementMoveStepsizeExt(int x, int y, int direction)
5048 {
5049   int element = Tile[x][y];
5050   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5051   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5052   int horiz_move = (dx != 0);
5053   int sign = (horiz_move ? dx : dy);
5054   int step = sign * element_info[element].move_stepsize;
5055
5056   // special values for move stepsize for spring and things on conveyor belt
5057   if (horiz_move)
5058   {
5059     if (CAN_FALL(element) &&
5060         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5061       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5062     else if (element == EL_SPRING)
5063       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5064   }
5065
5066   return step;
5067 }
5068
5069 static int getElementMoveStepsize(int x, int y)
5070 {
5071   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5072 }
5073
5074 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5075 {
5076   if (player->GfxAction != action || player->GfxDir != dir)
5077   {
5078     player->GfxAction = action;
5079     player->GfxDir = dir;
5080     player->Frame = 0;
5081     player->StepFrame = 0;
5082   }
5083 }
5084
5085 static void ResetGfxFrame(int x, int y)
5086 {
5087   // profiling showed that "autotest" spends 10~20% of its time in this function
5088   if (DrawingDeactivatedField())
5089     return;
5090
5091   int element = Tile[x][y];
5092   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5093
5094   if (graphic_info[graphic].anim_global_sync)
5095     GfxFrame[x][y] = FrameCounter;
5096   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5097     GfxFrame[x][y] = CustomValue[x][y];
5098   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5099     GfxFrame[x][y] = element_info[element].collect_score;
5100   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5101     GfxFrame[x][y] = ChangeDelay[x][y];
5102 }
5103
5104 static void ResetGfxAnimation(int x, int y)
5105 {
5106   GfxAction[x][y] = ACTION_DEFAULT;
5107   GfxDir[x][y] = MovDir[x][y];
5108   GfxFrame[x][y] = 0;
5109
5110   ResetGfxFrame(x, y);
5111 }
5112
5113 static void ResetRandomAnimationValue(int x, int y)
5114 {
5115   GfxRandom[x][y] = INIT_GFX_RANDOM();
5116 }
5117
5118 static void InitMovingField(int x, int y, int direction)
5119 {
5120   int element = Tile[x][y];
5121   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5122   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5123   int newx = x + dx;
5124   int newy = y + dy;
5125   boolean is_moving_before, is_moving_after;
5126
5127   // check if element was/is moving or being moved before/after mode change
5128   is_moving_before = (WasJustMoving[x][y] != 0);
5129   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5130
5131   // reset animation only for moving elements which change direction of moving
5132   // or which just started or stopped moving
5133   // (else CEs with property "can move" / "not moving" are reset each frame)
5134   if (is_moving_before != is_moving_after ||
5135       direction != MovDir[x][y])
5136     ResetGfxAnimation(x, y);
5137
5138   MovDir[x][y] = direction;
5139   GfxDir[x][y] = direction;
5140
5141   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5142                      direction == MV_DOWN && CAN_FALL(element) ?
5143                      ACTION_FALLING : ACTION_MOVING);
5144
5145   // this is needed for CEs with property "can move" / "not moving"
5146
5147   if (is_moving_after)
5148   {
5149     if (Tile[newx][newy] == EL_EMPTY)
5150       Tile[newx][newy] = EL_BLOCKED;
5151
5152     MovDir[newx][newy] = MovDir[x][y];
5153
5154     CustomValue[newx][newy] = CustomValue[x][y];
5155
5156     GfxFrame[newx][newy] = GfxFrame[x][y];
5157     GfxRandom[newx][newy] = GfxRandom[x][y];
5158     GfxAction[newx][newy] = GfxAction[x][y];
5159     GfxDir[newx][newy] = GfxDir[x][y];
5160   }
5161 }
5162
5163 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5164 {
5165   int direction = MovDir[x][y];
5166   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5167   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5168
5169   *goes_to_x = newx;
5170   *goes_to_y = newy;
5171 }
5172
5173 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5174 {
5175   int oldx = x, oldy = y;
5176   int direction = MovDir[x][y];
5177
5178   if (direction == MV_LEFT)
5179     oldx++;
5180   else if (direction == MV_RIGHT)
5181     oldx--;
5182   else if (direction == MV_UP)
5183     oldy++;
5184   else if (direction == MV_DOWN)
5185     oldy--;
5186
5187   *comes_from_x = oldx;
5188   *comes_from_y = oldy;
5189 }
5190
5191 static int MovingOrBlocked2Element(int x, int y)
5192 {
5193   int element = Tile[x][y];
5194
5195   if (element == EL_BLOCKED)
5196   {
5197     int oldx, oldy;
5198
5199     Blocked2Moving(x, y, &oldx, &oldy);
5200     return Tile[oldx][oldy];
5201   }
5202   else
5203     return element;
5204 }
5205
5206 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5207 {
5208   // like MovingOrBlocked2Element(), but if element is moving
5209   // and (x,y) is the field the moving element is just leaving,
5210   // return EL_BLOCKED instead of the element value
5211   int element = Tile[x][y];
5212
5213   if (IS_MOVING(x, y))
5214   {
5215     if (element == EL_BLOCKED)
5216     {
5217       int oldx, oldy;
5218
5219       Blocked2Moving(x, y, &oldx, &oldy);
5220       return Tile[oldx][oldy];
5221     }
5222     else
5223       return EL_BLOCKED;
5224   }
5225   else
5226     return element;
5227 }
5228
5229 static void RemoveField(int x, int y)
5230 {
5231   Tile[x][y] = EL_EMPTY;
5232
5233   MovPos[x][y] = 0;
5234   MovDir[x][y] = 0;
5235   MovDelay[x][y] = 0;
5236
5237   CustomValue[x][y] = 0;
5238
5239   AmoebaNr[x][y] = 0;
5240   ChangeDelay[x][y] = 0;
5241   ChangePage[x][y] = -1;
5242   Pushed[x][y] = FALSE;
5243
5244   GfxElement[x][y] = EL_UNDEFINED;
5245   GfxAction[x][y] = ACTION_DEFAULT;
5246   GfxDir[x][y] = MV_NONE;
5247 }
5248
5249 static void RemoveMovingField(int x, int y)
5250 {
5251   int oldx = x, oldy = y, newx = x, newy = y;
5252   int element = Tile[x][y];
5253   int next_element = EL_UNDEFINED;
5254
5255   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5256     return;
5257
5258   if (IS_MOVING(x, y))
5259   {
5260     Moving2Blocked(x, y, &newx, &newy);
5261
5262     if (Tile[newx][newy] != EL_BLOCKED)
5263     {
5264       // element is moving, but target field is not free (blocked), but
5265       // already occupied by something different (example: acid pool);
5266       // in this case, only remove the moving field, but not the target
5267
5268       RemoveField(oldx, oldy);
5269
5270       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5271
5272       TEST_DrawLevelField(oldx, oldy);
5273
5274       return;
5275     }
5276   }
5277   else if (element == EL_BLOCKED)
5278   {
5279     Blocked2Moving(x, y, &oldx, &oldy);
5280     if (!IS_MOVING(oldx, oldy))
5281       return;
5282   }
5283
5284   if (element == EL_BLOCKED &&
5285       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5286        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5287        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5288        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5289        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5290        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5291     next_element = get_next_element(Tile[oldx][oldy]);
5292
5293   RemoveField(oldx, oldy);
5294   RemoveField(newx, newy);
5295
5296   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5297
5298   if (next_element != EL_UNDEFINED)
5299     Tile[oldx][oldy] = next_element;
5300
5301   TEST_DrawLevelField(oldx, oldy);
5302   TEST_DrawLevelField(newx, newy);
5303 }
5304
5305 void DrawDynamite(int x, int y)
5306 {
5307   int sx = SCREENX(x), sy = SCREENY(y);
5308   int graphic = el2img(Tile[x][y]);
5309   int frame;
5310
5311   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5312     return;
5313
5314   if (IS_WALKABLE_INSIDE(Back[x][y]))
5315     return;
5316
5317   if (Back[x][y])
5318     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5319   else if (Store[x][y])
5320     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5321
5322   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5323
5324   if (Back[x][y] || Store[x][y])
5325     DrawGraphicThruMask(sx, sy, graphic, frame);
5326   else
5327     DrawGraphic(sx, sy, graphic, frame);
5328 }
5329
5330 static void CheckDynamite(int x, int y)
5331 {
5332   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5333   {
5334     MovDelay[x][y]--;
5335
5336     if (MovDelay[x][y] != 0)
5337     {
5338       DrawDynamite(x, y);
5339       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5340
5341       return;
5342     }
5343   }
5344
5345   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5346
5347   Bang(x, y);
5348 }
5349
5350 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5351 {
5352   boolean num_checked_players = 0;
5353   int i;
5354
5355   for (i = 0; i < MAX_PLAYERS; i++)
5356   {
5357     if (stored_player[i].active)
5358     {
5359       int sx = stored_player[i].jx;
5360       int sy = stored_player[i].jy;
5361
5362       if (num_checked_players == 0)
5363       {
5364         *sx1 = *sx2 = sx;
5365         *sy1 = *sy2 = sy;
5366       }
5367       else
5368       {
5369         *sx1 = MIN(*sx1, sx);
5370         *sy1 = MIN(*sy1, sy);
5371         *sx2 = MAX(*sx2, sx);
5372         *sy2 = MAX(*sy2, sy);
5373       }
5374
5375       num_checked_players++;
5376     }
5377   }
5378 }
5379
5380 static boolean checkIfAllPlayersFitToScreen_RND(void)
5381 {
5382   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5383
5384   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5385
5386   return (sx2 - sx1 < SCR_FIELDX &&
5387           sy2 - sy1 < SCR_FIELDY);
5388 }
5389
5390 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5391 {
5392   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5393
5394   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5395
5396   *sx = (sx1 + sx2) / 2;
5397   *sy = (sy1 + sy2) / 2;
5398 }
5399
5400 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5401                                boolean center_screen, boolean quick_relocation)
5402 {
5403   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5404   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5405   boolean no_delay = (tape.warp_forward);
5406   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5407   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5408   int new_scroll_x, new_scroll_y;
5409
5410   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5411   {
5412     // case 1: quick relocation inside visible screen (without scrolling)
5413
5414     RedrawPlayfield();
5415
5416     return;
5417   }
5418
5419   if (!level.shifted_relocation || center_screen)
5420   {
5421     // relocation _with_ centering of screen
5422
5423     new_scroll_x = SCROLL_POSITION_X(x);
5424     new_scroll_y = SCROLL_POSITION_Y(y);
5425   }
5426   else
5427   {
5428     // relocation _without_ centering of screen
5429
5430     int center_scroll_x = SCROLL_POSITION_X(old_x);
5431     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5432     int offset_x = x + (scroll_x - center_scroll_x);
5433     int offset_y = y + (scroll_y - center_scroll_y);
5434
5435     // for new screen position, apply previous offset to center position
5436     new_scroll_x = SCROLL_POSITION_X(offset_x);
5437     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5438   }
5439
5440   if (quick_relocation)
5441   {
5442     // case 2: quick relocation (redraw without visible scrolling)
5443
5444     scroll_x = new_scroll_x;
5445     scroll_y = new_scroll_y;
5446
5447     RedrawPlayfield();
5448
5449     return;
5450   }
5451
5452   // case 3: visible relocation (with scrolling to new position)
5453
5454   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5455
5456   SetVideoFrameDelay(wait_delay_value);
5457
5458   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5459   {
5460     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5461     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5462
5463     if (dx == 0 && dy == 0)             // no scrolling needed at all
5464       break;
5465
5466     scroll_x -= dx;
5467     scroll_y -= dy;
5468
5469     // set values for horizontal/vertical screen scrolling (half tile size)
5470     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5471     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5472     int pos_x = dx * TILEX / 2;
5473     int pos_y = dy * TILEY / 2;
5474     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5475     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5476
5477     ScrollLevel(dx, dy);
5478     DrawAllPlayers();
5479
5480     // scroll in two steps of half tile size to make things smoother
5481     BlitScreenToBitmapExt_RND(window, fx, fy);
5482
5483     // scroll second step to align at full tile size
5484     BlitScreenToBitmap(window);
5485   }
5486
5487   DrawAllPlayers();
5488   BackToFront();
5489
5490   SetVideoFrameDelay(frame_delay_value_old);
5491 }
5492
5493 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5494 {
5495   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5496   int player_nr = GET_PLAYER_NR(el_player);
5497   struct PlayerInfo *player = &stored_player[player_nr];
5498   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5499   boolean no_delay = (tape.warp_forward);
5500   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5501   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5502   int old_jx = player->jx;
5503   int old_jy = player->jy;
5504   int old_element = Tile[old_jx][old_jy];
5505   int element = Tile[jx][jy];
5506   boolean player_relocated = (old_jx != jx || old_jy != jy);
5507
5508   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5509   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5510   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5511   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5512   int leave_side_horiz = move_dir_horiz;
5513   int leave_side_vert  = move_dir_vert;
5514   int enter_side = enter_side_horiz | enter_side_vert;
5515   int leave_side = leave_side_horiz | leave_side_vert;
5516
5517   if (player->buried)           // do not reanimate dead player
5518     return;
5519
5520   if (!player_relocated)        // no need to relocate the player
5521     return;
5522
5523   if (IS_PLAYER(jx, jy))        // player already placed at new position
5524   {
5525     RemoveField(jx, jy);        // temporarily remove newly placed player
5526     DrawLevelField(jx, jy);
5527   }
5528
5529   if (player->present)
5530   {
5531     while (player->MovPos)
5532     {
5533       ScrollPlayer(player, SCROLL_GO_ON);
5534       ScrollScreen(NULL, SCROLL_GO_ON);
5535
5536       AdvanceFrameAndPlayerCounters(player->index_nr);
5537
5538       DrawPlayer(player);
5539
5540       BackToFront_WithFrameDelay(wait_delay_value);
5541     }
5542
5543     DrawPlayer(player);         // needed here only to cleanup last field
5544     DrawLevelField(player->jx, player->jy);     // remove player graphic
5545
5546     player->is_moving = FALSE;
5547   }
5548
5549   if (IS_CUSTOM_ELEMENT(old_element))
5550     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5551                                CE_LEFT_BY_PLAYER,
5552                                player->index_bit, leave_side);
5553
5554   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5555                                       CE_PLAYER_LEAVES_X,
5556                                       player->index_bit, leave_side);
5557
5558   Tile[jx][jy] = el_player;
5559   InitPlayerField(jx, jy, el_player, TRUE);
5560
5561   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5562      possible that the relocation target field did not contain a player element,
5563      but a walkable element, to which the new player was relocated -- in this
5564      case, restore that (already initialized!) element on the player field */
5565   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5566   {
5567     Tile[jx][jy] = element;     // restore previously existing element
5568   }
5569
5570   // only visually relocate centered player
5571   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5572                      FALSE, level.instant_relocation);
5573
5574   TestIfPlayerTouchesBadThing(jx, jy);
5575   TestIfPlayerTouchesCustomElement(jx, jy);
5576
5577   if (IS_CUSTOM_ELEMENT(element))
5578     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5579                                player->index_bit, enter_side);
5580
5581   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5582                                       player->index_bit, enter_side);
5583
5584   if (player->is_switching)
5585   {
5586     /* ensure that relocation while still switching an element does not cause
5587        a new element to be treated as also switched directly after relocation
5588        (this is important for teleporter switches that teleport the player to
5589        a place where another teleporter switch is in the same direction, which
5590        would then incorrectly be treated as immediately switched before the
5591        direction key that caused the switch was released) */
5592
5593     player->switch_x += jx - old_jx;
5594     player->switch_y += jy - old_jy;
5595   }
5596 }
5597
5598 static void Explode(int ex, int ey, int phase, int mode)
5599 {
5600   int x, y;
5601   int last_phase;
5602   int border_element;
5603
5604   // !!! eliminate this variable !!!
5605   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5606
5607   if (game.explosions_delayed)
5608   {
5609     ExplodeField[ex][ey] = mode;
5610     return;
5611   }
5612
5613   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5614   {
5615     int center_element = Tile[ex][ey];
5616     int artwork_element, explosion_element;     // set these values later
5617
5618     // remove things displayed in background while burning dynamite
5619     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5620       Back[ex][ey] = 0;
5621
5622     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5623     {
5624       // put moving element to center field (and let it explode there)
5625       center_element = MovingOrBlocked2Element(ex, ey);
5626       RemoveMovingField(ex, ey);
5627       Tile[ex][ey] = center_element;
5628     }
5629
5630     // now "center_element" is finally determined -- set related values now
5631     artwork_element = center_element;           // for custom player artwork
5632     explosion_element = center_element;         // for custom player artwork
5633
5634     if (IS_PLAYER(ex, ey))
5635     {
5636       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5637
5638       artwork_element = stored_player[player_nr].artwork_element;
5639
5640       if (level.use_explosion_element[player_nr])
5641       {
5642         explosion_element = level.explosion_element[player_nr];
5643         artwork_element = explosion_element;
5644       }
5645     }
5646
5647     if (mode == EX_TYPE_NORMAL ||
5648         mode == EX_TYPE_CENTER ||
5649         mode == EX_TYPE_CROSS)
5650       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5651
5652     last_phase = element_info[explosion_element].explosion_delay + 1;
5653
5654     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5655     {
5656       int xx = x - ex + 1;
5657       int yy = y - ey + 1;
5658       int element;
5659
5660       if (!IN_LEV_FIELD(x, y) ||
5661           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5662           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5663         continue;
5664
5665       element = Tile[x][y];
5666
5667       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5668       {
5669         element = MovingOrBlocked2Element(x, y);
5670
5671         if (!IS_EXPLOSION_PROOF(element))
5672           RemoveMovingField(x, y);
5673       }
5674
5675       // indestructible elements can only explode in center (but not flames)
5676       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5677                                            mode == EX_TYPE_BORDER)) ||
5678           element == EL_FLAMES)
5679         continue;
5680
5681       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5682          behaviour, for example when touching a yamyam that explodes to rocks
5683          with active deadly shield, a rock is created under the player !!! */
5684       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5685 #if 0
5686       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5687           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5688            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5689 #else
5690       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5691 #endif
5692       {
5693         if (IS_ACTIVE_BOMB(element))
5694         {
5695           // re-activate things under the bomb like gate or penguin
5696           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5697           Back[x][y] = 0;
5698         }
5699
5700         continue;
5701       }
5702
5703       // save walkable background elements while explosion on same tile
5704       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5705           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5706         Back[x][y] = element;
5707
5708       // ignite explodable elements reached by other explosion
5709       if (element == EL_EXPLOSION)
5710         element = Store2[x][y];
5711
5712       if (AmoebaNr[x][y] &&
5713           (element == EL_AMOEBA_FULL ||
5714            element == EL_BD_AMOEBA ||
5715            element == EL_AMOEBA_GROWING))
5716       {
5717         AmoebaCnt[AmoebaNr[x][y]]--;
5718         AmoebaCnt2[AmoebaNr[x][y]]--;
5719       }
5720
5721       RemoveField(x, y);
5722
5723       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5724       {
5725         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5726
5727         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5728
5729         if (PLAYERINFO(ex, ey)->use_murphy)
5730           Store[x][y] = EL_EMPTY;
5731       }
5732
5733       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5734       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5735       else if (ELEM_IS_PLAYER(center_element))
5736         Store[x][y] = EL_EMPTY;
5737       else if (center_element == EL_YAMYAM)
5738         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5739       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5740         Store[x][y] = element_info[center_element].content.e[xx][yy];
5741 #if 1
5742       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5743       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5744       // otherwise) -- FIX THIS !!!
5745       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5746         Store[x][y] = element_info[element].content.e[1][1];
5747 #else
5748       else if (!CAN_EXPLODE(element))
5749         Store[x][y] = element_info[element].content.e[1][1];
5750 #endif
5751       else
5752         Store[x][y] = EL_EMPTY;
5753
5754       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5755           center_element == EL_AMOEBA_TO_DIAMOND)
5756         Store2[x][y] = element;
5757
5758       Tile[x][y] = EL_EXPLOSION;
5759       GfxElement[x][y] = artwork_element;
5760
5761       ExplodePhase[x][y] = 1;
5762       ExplodeDelay[x][y] = last_phase;
5763
5764       Stop[x][y] = TRUE;
5765     }
5766
5767     if (center_element == EL_YAMYAM)
5768       game.yamyam_content_nr =
5769         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5770
5771     return;
5772   }
5773
5774   if (Stop[ex][ey])
5775     return;
5776
5777   x = ex;
5778   y = ey;
5779
5780   if (phase == 1)
5781     GfxFrame[x][y] = 0;         // restart explosion animation
5782
5783   last_phase = ExplodeDelay[x][y];
5784
5785   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5786
5787   // this can happen if the player leaves an explosion just in time
5788   if (GfxElement[x][y] == EL_UNDEFINED)
5789     GfxElement[x][y] = EL_EMPTY;
5790
5791   border_element = Store2[x][y];
5792   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5793     border_element = StorePlayer[x][y];
5794
5795   if (phase == element_info[border_element].ignition_delay ||
5796       phase == last_phase)
5797   {
5798     boolean border_explosion = FALSE;
5799
5800     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5801         !PLAYER_EXPLOSION_PROTECTED(x, y))
5802     {
5803       KillPlayerUnlessExplosionProtected(x, y);
5804       border_explosion = TRUE;
5805     }
5806     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5807     {
5808       Tile[x][y] = Store2[x][y];
5809       Store2[x][y] = 0;
5810       Bang(x, y);
5811       border_explosion = TRUE;
5812     }
5813     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5814     {
5815       AmoebaToDiamond(x, y);
5816       Store2[x][y] = 0;
5817       border_explosion = TRUE;
5818     }
5819
5820     // if an element just explodes due to another explosion (chain-reaction),
5821     // do not immediately end the new explosion when it was the last frame of
5822     // the explosion (as it would be done in the following "if"-statement!)
5823     if (border_explosion && phase == last_phase)
5824       return;
5825   }
5826
5827   if (phase == last_phase)
5828   {
5829     int element;
5830
5831     element = Tile[x][y] = Store[x][y];
5832     Store[x][y] = Store2[x][y] = 0;
5833     GfxElement[x][y] = EL_UNDEFINED;
5834
5835     // player can escape from explosions and might therefore be still alive
5836     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5837         element <= EL_PLAYER_IS_EXPLODING_4)
5838     {
5839       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5840       int explosion_element = EL_PLAYER_1 + player_nr;
5841       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5842       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5843
5844       if (level.use_explosion_element[player_nr])
5845         explosion_element = level.explosion_element[player_nr];
5846
5847       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5848                     element_info[explosion_element].content.e[xx][yy]);
5849     }
5850
5851     // restore probably existing indestructible background element
5852     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5853       element = Tile[x][y] = Back[x][y];
5854     Back[x][y] = 0;
5855
5856     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5857     GfxDir[x][y] = MV_NONE;
5858     ChangeDelay[x][y] = 0;
5859     ChangePage[x][y] = -1;
5860
5861     CustomValue[x][y] = 0;
5862
5863     InitField_WithBug2(x, y, FALSE);
5864
5865     TEST_DrawLevelField(x, y);
5866
5867     TestIfElementTouchesCustomElement(x, y);
5868
5869     if (GFX_CRUMBLED(element))
5870       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5871
5872     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5873       StorePlayer[x][y] = 0;
5874
5875     if (ELEM_IS_PLAYER(element))
5876       RelocatePlayer(x, y, element);
5877   }
5878   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5879   {
5880     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5881     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5882
5883     if (phase == delay)
5884       TEST_DrawLevelFieldCrumbled(x, y);
5885
5886     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5887     {
5888       DrawLevelElement(x, y, Back[x][y]);
5889       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5890     }
5891     else if (IS_WALKABLE_UNDER(Back[x][y]))
5892     {
5893       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5894       DrawLevelElementThruMask(x, y, Back[x][y]);
5895     }
5896     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5897       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5898   }
5899 }
5900
5901 static void DynaExplode(int ex, int ey)
5902 {
5903   int i, j;
5904   int dynabomb_element = Tile[ex][ey];
5905   int dynabomb_size = 1;
5906   boolean dynabomb_xl = FALSE;
5907   struct PlayerInfo *player;
5908   static int xy[4][2] =
5909   {
5910     { 0, -1 },
5911     { -1, 0 },
5912     { +1, 0 },
5913     { 0, +1 }
5914   };
5915
5916   if (IS_ACTIVE_BOMB(dynabomb_element))
5917   {
5918     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5919     dynabomb_size = player->dynabomb_size;
5920     dynabomb_xl = player->dynabomb_xl;
5921     player->dynabombs_left++;
5922   }
5923
5924   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5925
5926   for (i = 0; i < NUM_DIRECTIONS; i++)
5927   {
5928     for (j = 1; j <= dynabomb_size; j++)
5929     {
5930       int x = ex + j * xy[i][0];
5931       int y = ey + j * xy[i][1];
5932       int element;
5933
5934       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
5935         break;
5936
5937       element = Tile[x][y];
5938
5939       // do not restart explosions of fields with active bombs
5940       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5941         continue;
5942
5943       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5944
5945       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5946           !IS_DIGGABLE(element) && !dynabomb_xl)
5947         break;
5948     }
5949   }
5950 }
5951
5952 void Bang(int x, int y)
5953 {
5954   int element = MovingOrBlocked2Element(x, y);
5955   int explosion_type = EX_TYPE_NORMAL;
5956
5957   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5958   {
5959     struct PlayerInfo *player = PLAYERINFO(x, y);
5960
5961     element = Tile[x][y] = player->initial_element;
5962
5963     if (level.use_explosion_element[player->index_nr])
5964     {
5965       int explosion_element = level.explosion_element[player->index_nr];
5966
5967       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5968         explosion_type = EX_TYPE_CROSS;
5969       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5970         explosion_type = EX_TYPE_CENTER;
5971     }
5972   }
5973
5974   switch (element)
5975   {
5976     case EL_BUG:
5977     case EL_SPACESHIP:
5978     case EL_BD_BUTTERFLY:
5979     case EL_BD_FIREFLY:
5980     case EL_YAMYAM:
5981     case EL_DARK_YAMYAM:
5982     case EL_ROBOT:
5983     case EL_PACMAN:
5984     case EL_MOLE:
5985       RaiseScoreElement(element);
5986       break;
5987
5988     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5989     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5990     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5991     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5992     case EL_DYNABOMB_INCREASE_NUMBER:
5993     case EL_DYNABOMB_INCREASE_SIZE:
5994     case EL_DYNABOMB_INCREASE_POWER:
5995       explosion_type = EX_TYPE_DYNA;
5996       break;
5997
5998     case EL_DC_LANDMINE:
5999       explosion_type = EX_TYPE_CENTER;
6000       break;
6001
6002     case EL_PENGUIN:
6003     case EL_LAMP:
6004     case EL_LAMP_ACTIVE:
6005     case EL_AMOEBA_TO_DIAMOND:
6006       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6007         explosion_type = EX_TYPE_CENTER;
6008       break;
6009
6010     default:
6011       if (element_info[element].explosion_type == EXPLODES_CROSS)
6012         explosion_type = EX_TYPE_CROSS;
6013       else if (element_info[element].explosion_type == EXPLODES_1X1)
6014         explosion_type = EX_TYPE_CENTER;
6015       break;
6016   }
6017
6018   if (explosion_type == EX_TYPE_DYNA)
6019     DynaExplode(x, y);
6020   else
6021     Explode(x, y, EX_PHASE_START, explosion_type);
6022
6023   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6024 }
6025
6026 static void SplashAcid(int x, int y)
6027 {
6028   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6029       (!IN_LEV_FIELD(x - 1, y - 2) ||
6030        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6031     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6032
6033   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6034       (!IN_LEV_FIELD(x + 1, y - 2) ||
6035        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6036     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6037
6038   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6039 }
6040
6041 static void InitBeltMovement(void)
6042 {
6043   static int belt_base_element[4] =
6044   {
6045     EL_CONVEYOR_BELT_1_LEFT,
6046     EL_CONVEYOR_BELT_2_LEFT,
6047     EL_CONVEYOR_BELT_3_LEFT,
6048     EL_CONVEYOR_BELT_4_LEFT
6049   };
6050   static int belt_base_active_element[4] =
6051   {
6052     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6053     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6054     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6055     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6056   };
6057
6058   int x, y, i, j;
6059
6060   // set frame order for belt animation graphic according to belt direction
6061   for (i = 0; i < NUM_BELTS; i++)
6062   {
6063     int belt_nr = i;
6064
6065     for (j = 0; j < NUM_BELT_PARTS; j++)
6066     {
6067       int element = belt_base_active_element[belt_nr] + j;
6068       int graphic_1 = el2img(element);
6069       int graphic_2 = el2panelimg(element);
6070
6071       if (game.belt_dir[i] == MV_LEFT)
6072       {
6073         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6074         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6075       }
6076       else
6077       {
6078         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6079         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6080       }
6081     }
6082   }
6083
6084   SCAN_PLAYFIELD(x, y)
6085   {
6086     int element = Tile[x][y];
6087
6088     for (i = 0; i < NUM_BELTS; i++)
6089     {
6090       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6091       {
6092         int e_belt_nr = getBeltNrFromBeltElement(element);
6093         int belt_nr = i;
6094
6095         if (e_belt_nr == belt_nr)
6096         {
6097           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6098
6099           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6100         }
6101       }
6102     }
6103   }
6104 }
6105
6106 static void ToggleBeltSwitch(int x, int y)
6107 {
6108   static int belt_base_element[4] =
6109   {
6110     EL_CONVEYOR_BELT_1_LEFT,
6111     EL_CONVEYOR_BELT_2_LEFT,
6112     EL_CONVEYOR_BELT_3_LEFT,
6113     EL_CONVEYOR_BELT_4_LEFT
6114   };
6115   static int belt_base_active_element[4] =
6116   {
6117     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6118     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6119     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6120     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6121   };
6122   static int belt_base_switch_element[4] =
6123   {
6124     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6125     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6126     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6127     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6128   };
6129   static int belt_move_dir[4] =
6130   {
6131     MV_LEFT,
6132     MV_NONE,
6133     MV_RIGHT,
6134     MV_NONE,
6135   };
6136
6137   int element = Tile[x][y];
6138   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6139   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6140   int belt_dir = belt_move_dir[belt_dir_nr];
6141   int xx, yy, i;
6142
6143   if (!IS_BELT_SWITCH(element))
6144     return;
6145
6146   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6147   game.belt_dir[belt_nr] = belt_dir;
6148
6149   if (belt_dir_nr == 3)
6150     belt_dir_nr = 1;
6151
6152   // set frame order for belt animation graphic according to belt direction
6153   for (i = 0; i < NUM_BELT_PARTS; i++)
6154   {
6155     int element = belt_base_active_element[belt_nr] + i;
6156     int graphic_1 = el2img(element);
6157     int graphic_2 = el2panelimg(element);
6158
6159     if (belt_dir == MV_LEFT)
6160     {
6161       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6162       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6163     }
6164     else
6165     {
6166       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6167       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6168     }
6169   }
6170
6171   SCAN_PLAYFIELD(xx, yy)
6172   {
6173     int element = Tile[xx][yy];
6174
6175     if (IS_BELT_SWITCH(element))
6176     {
6177       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6178
6179       if (e_belt_nr == belt_nr)
6180       {
6181         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6182         TEST_DrawLevelField(xx, yy);
6183       }
6184     }
6185     else if (IS_BELT(element) && belt_dir != MV_NONE)
6186     {
6187       int e_belt_nr = getBeltNrFromBeltElement(element);
6188
6189       if (e_belt_nr == belt_nr)
6190       {
6191         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6192
6193         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6194         TEST_DrawLevelField(xx, yy);
6195       }
6196     }
6197     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6198     {
6199       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6200
6201       if (e_belt_nr == belt_nr)
6202       {
6203         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6204
6205         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6206         TEST_DrawLevelField(xx, yy);
6207       }
6208     }
6209   }
6210 }
6211
6212 static void ToggleSwitchgateSwitch(int x, int y)
6213 {
6214   int xx, yy;
6215
6216   game.switchgate_pos = !game.switchgate_pos;
6217
6218   SCAN_PLAYFIELD(xx, yy)
6219   {
6220     int element = Tile[xx][yy];
6221
6222     if (element == EL_SWITCHGATE_SWITCH_UP)
6223     {
6224       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6225       TEST_DrawLevelField(xx, yy);
6226     }
6227     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6228     {
6229       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6230       TEST_DrawLevelField(xx, yy);
6231     }
6232     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6233     {
6234       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6235       TEST_DrawLevelField(xx, yy);
6236     }
6237     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6238     {
6239       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6240       TEST_DrawLevelField(xx, yy);
6241     }
6242     else if (element == EL_SWITCHGATE_OPEN ||
6243              element == EL_SWITCHGATE_OPENING)
6244     {
6245       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6246
6247       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6248     }
6249     else if (element == EL_SWITCHGATE_CLOSED ||
6250              element == EL_SWITCHGATE_CLOSING)
6251     {
6252       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6253
6254       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6255     }
6256   }
6257 }
6258
6259 static int getInvisibleActiveFromInvisibleElement(int element)
6260 {
6261   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6262           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6263           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6264           element);
6265 }
6266
6267 static int getInvisibleFromInvisibleActiveElement(int element)
6268 {
6269   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6270           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6271           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6272           element);
6273 }
6274
6275 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6276 {
6277   int x, y;
6278
6279   SCAN_PLAYFIELD(x, y)
6280   {
6281     int element = Tile[x][y];
6282
6283     if (element == EL_LIGHT_SWITCH &&
6284         game.light_time_left > 0)
6285     {
6286       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6287       TEST_DrawLevelField(x, y);
6288     }
6289     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6290              game.light_time_left == 0)
6291     {
6292       Tile[x][y] = EL_LIGHT_SWITCH;
6293       TEST_DrawLevelField(x, y);
6294     }
6295     else if (element == EL_EMC_DRIPPER &&
6296              game.light_time_left > 0)
6297     {
6298       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6299       TEST_DrawLevelField(x, y);
6300     }
6301     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6302              game.light_time_left == 0)
6303     {
6304       Tile[x][y] = EL_EMC_DRIPPER;
6305       TEST_DrawLevelField(x, y);
6306     }
6307     else if (element == EL_INVISIBLE_STEELWALL ||
6308              element == EL_INVISIBLE_WALL ||
6309              element == EL_INVISIBLE_SAND)
6310     {
6311       if (game.light_time_left > 0)
6312         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6313
6314       TEST_DrawLevelField(x, y);
6315
6316       // uncrumble neighbour fields, if needed
6317       if (element == EL_INVISIBLE_SAND)
6318         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6319     }
6320     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6321              element == EL_INVISIBLE_WALL_ACTIVE ||
6322              element == EL_INVISIBLE_SAND_ACTIVE)
6323     {
6324       if (game.light_time_left == 0)
6325         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6326
6327       TEST_DrawLevelField(x, y);
6328
6329       // re-crumble neighbour fields, if needed
6330       if (element == EL_INVISIBLE_SAND)
6331         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6332     }
6333   }
6334 }
6335
6336 static void RedrawAllInvisibleElementsForLenses(void)
6337 {
6338   int x, y;
6339
6340   SCAN_PLAYFIELD(x, y)
6341   {
6342     int element = Tile[x][y];
6343
6344     if (element == EL_EMC_DRIPPER &&
6345         game.lenses_time_left > 0)
6346     {
6347       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6348       TEST_DrawLevelField(x, y);
6349     }
6350     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6351              game.lenses_time_left == 0)
6352     {
6353       Tile[x][y] = EL_EMC_DRIPPER;
6354       TEST_DrawLevelField(x, y);
6355     }
6356     else if (element == EL_INVISIBLE_STEELWALL ||
6357              element == EL_INVISIBLE_WALL ||
6358              element == EL_INVISIBLE_SAND)
6359     {
6360       if (game.lenses_time_left > 0)
6361         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6362
6363       TEST_DrawLevelField(x, y);
6364
6365       // uncrumble neighbour fields, if needed
6366       if (element == EL_INVISIBLE_SAND)
6367         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6368     }
6369     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6370              element == EL_INVISIBLE_WALL_ACTIVE ||
6371              element == EL_INVISIBLE_SAND_ACTIVE)
6372     {
6373       if (game.lenses_time_left == 0)
6374         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6375
6376       TEST_DrawLevelField(x, y);
6377
6378       // re-crumble neighbour fields, if needed
6379       if (element == EL_INVISIBLE_SAND)
6380         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6381     }
6382   }
6383 }
6384
6385 static void RedrawAllInvisibleElementsForMagnifier(void)
6386 {
6387   int x, y;
6388
6389   SCAN_PLAYFIELD(x, y)
6390   {
6391     int element = Tile[x][y];
6392
6393     if (element == EL_EMC_FAKE_GRASS &&
6394         game.magnify_time_left > 0)
6395     {
6396       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6397       TEST_DrawLevelField(x, y);
6398     }
6399     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6400              game.magnify_time_left == 0)
6401     {
6402       Tile[x][y] = EL_EMC_FAKE_GRASS;
6403       TEST_DrawLevelField(x, y);
6404     }
6405     else if (IS_GATE_GRAY(element) &&
6406              game.magnify_time_left > 0)
6407     {
6408       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6409                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6410                     IS_EM_GATE_GRAY(element) ?
6411                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6412                     IS_EMC_GATE_GRAY(element) ?
6413                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6414                     IS_DC_GATE_GRAY(element) ?
6415                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6416                     element);
6417       TEST_DrawLevelField(x, y);
6418     }
6419     else if (IS_GATE_GRAY_ACTIVE(element) &&
6420              game.magnify_time_left == 0)
6421     {
6422       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6423                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6424                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6425                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6426                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6427                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6428                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6429                     EL_DC_GATE_WHITE_GRAY :
6430                     element);
6431       TEST_DrawLevelField(x, y);
6432     }
6433   }
6434 }
6435
6436 static void ToggleLightSwitch(int x, int y)
6437 {
6438   int element = Tile[x][y];
6439
6440   game.light_time_left =
6441     (element == EL_LIGHT_SWITCH ?
6442      level.time_light * FRAMES_PER_SECOND : 0);
6443
6444   RedrawAllLightSwitchesAndInvisibleElements();
6445 }
6446
6447 static void ActivateTimegateSwitch(int x, int y)
6448 {
6449   int xx, yy;
6450
6451   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6452
6453   SCAN_PLAYFIELD(xx, yy)
6454   {
6455     int element = Tile[xx][yy];
6456
6457     if (element == EL_TIMEGATE_CLOSED ||
6458         element == EL_TIMEGATE_CLOSING)
6459     {
6460       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6461       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6462     }
6463
6464     /*
6465     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6466     {
6467       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6468       TEST_DrawLevelField(xx, yy);
6469     }
6470     */
6471
6472   }
6473
6474   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6475                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6476 }
6477
6478 static void Impact(int x, int y)
6479 {
6480   boolean last_line = (y == lev_fieldy - 1);
6481   boolean object_hit = FALSE;
6482   boolean impact = (last_line || object_hit);
6483   int element = Tile[x][y];
6484   int smashed = EL_STEELWALL;
6485
6486   if (!last_line)       // check if element below was hit
6487   {
6488     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6489       return;
6490
6491     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6492                                          MovDir[x][y + 1] != MV_DOWN ||
6493                                          MovPos[x][y + 1] <= TILEY / 2));
6494
6495     // do not smash moving elements that left the smashed field in time
6496     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6497         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6498       object_hit = FALSE;
6499
6500 #if USE_QUICKSAND_IMPACT_BUGFIX
6501     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6502     {
6503       RemoveMovingField(x, y + 1);
6504       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6505       Tile[x][y + 2] = EL_ROCK;
6506       TEST_DrawLevelField(x, y + 2);
6507
6508       object_hit = TRUE;
6509     }
6510
6511     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6512     {
6513       RemoveMovingField(x, y + 1);
6514       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6515       Tile[x][y + 2] = EL_ROCK;
6516       TEST_DrawLevelField(x, y + 2);
6517
6518       object_hit = TRUE;
6519     }
6520 #endif
6521
6522     if (object_hit)
6523       smashed = MovingOrBlocked2Element(x, y + 1);
6524
6525     impact = (last_line || object_hit);
6526   }
6527
6528   if (!last_line && smashed == EL_ACID) // element falls into acid
6529   {
6530     SplashAcid(x, y + 1);
6531     return;
6532   }
6533
6534   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6535   // only reset graphic animation if graphic really changes after impact
6536   if (impact &&
6537       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6538   {
6539     ResetGfxAnimation(x, y);
6540     TEST_DrawLevelField(x, y);
6541   }
6542
6543   if (impact && CAN_EXPLODE_IMPACT(element))
6544   {
6545     Bang(x, y);
6546     return;
6547   }
6548   else if (impact && element == EL_PEARL &&
6549            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6550   {
6551     ResetGfxAnimation(x, y);
6552
6553     Tile[x][y] = EL_PEARL_BREAKING;
6554     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6555     return;
6556   }
6557   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6558   {
6559     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6560
6561     return;
6562   }
6563
6564   if (impact && element == EL_AMOEBA_DROP)
6565   {
6566     if (object_hit && IS_PLAYER(x, y + 1))
6567       KillPlayerUnlessEnemyProtected(x, y + 1);
6568     else if (object_hit && smashed == EL_PENGUIN)
6569       Bang(x, y + 1);
6570     else
6571     {
6572       Tile[x][y] = EL_AMOEBA_GROWING;
6573       Store[x][y] = EL_AMOEBA_WET;
6574
6575       ResetRandomAnimationValue(x, y);
6576     }
6577     return;
6578   }
6579
6580   if (object_hit)               // check which object was hit
6581   {
6582     if ((CAN_PASS_MAGIC_WALL(element) && 
6583          (smashed == EL_MAGIC_WALL ||
6584           smashed == EL_BD_MAGIC_WALL)) ||
6585         (CAN_PASS_DC_MAGIC_WALL(element) &&
6586          smashed == EL_DC_MAGIC_WALL))
6587     {
6588       int xx, yy;
6589       int activated_magic_wall =
6590         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6591          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6592          EL_DC_MAGIC_WALL_ACTIVE);
6593
6594       // activate magic wall / mill
6595       SCAN_PLAYFIELD(xx, yy)
6596       {
6597         if (Tile[xx][yy] == smashed)
6598           Tile[xx][yy] = activated_magic_wall;
6599       }
6600
6601       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6602       game.magic_wall_active = TRUE;
6603
6604       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6605                             SND_MAGIC_WALL_ACTIVATING :
6606                             smashed == EL_BD_MAGIC_WALL ?
6607                             SND_BD_MAGIC_WALL_ACTIVATING :
6608                             SND_DC_MAGIC_WALL_ACTIVATING));
6609     }
6610
6611     if (IS_PLAYER(x, y + 1))
6612     {
6613       if (CAN_SMASH_PLAYER(element))
6614       {
6615         KillPlayerUnlessEnemyProtected(x, y + 1);
6616         return;
6617       }
6618     }
6619     else if (smashed == EL_PENGUIN)
6620     {
6621       if (CAN_SMASH_PLAYER(element))
6622       {
6623         Bang(x, y + 1);
6624         return;
6625       }
6626     }
6627     else if (element == EL_BD_DIAMOND)
6628     {
6629       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6630       {
6631         Bang(x, y + 1);
6632         return;
6633       }
6634     }
6635     else if (((element == EL_SP_INFOTRON ||
6636                element == EL_SP_ZONK) &&
6637               (smashed == EL_SP_SNIKSNAK ||
6638                smashed == EL_SP_ELECTRON ||
6639                smashed == EL_SP_DISK_ORANGE)) ||
6640              (element == EL_SP_INFOTRON &&
6641               smashed == EL_SP_DISK_YELLOW))
6642     {
6643       Bang(x, y + 1);
6644       return;
6645     }
6646     else if (CAN_SMASH_EVERYTHING(element))
6647     {
6648       if (IS_CLASSIC_ENEMY(smashed) ||
6649           CAN_EXPLODE_SMASHED(smashed))
6650       {
6651         Bang(x, y + 1);
6652         return;
6653       }
6654       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6655       {
6656         if (smashed == EL_LAMP ||
6657             smashed == EL_LAMP_ACTIVE)
6658         {
6659           Bang(x, y + 1);
6660           return;
6661         }
6662         else if (smashed == EL_NUT)
6663         {
6664           Tile[x][y + 1] = EL_NUT_BREAKING;
6665           PlayLevelSound(x, y, SND_NUT_BREAKING);
6666           RaiseScoreElement(EL_NUT);
6667           return;
6668         }
6669         else if (smashed == EL_PEARL)
6670         {
6671           ResetGfxAnimation(x, y);
6672
6673           Tile[x][y + 1] = EL_PEARL_BREAKING;
6674           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6675           return;
6676         }
6677         else if (smashed == EL_DIAMOND)
6678         {
6679           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6680           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6681           return;
6682         }
6683         else if (IS_BELT_SWITCH(smashed))
6684         {
6685           ToggleBeltSwitch(x, y + 1);
6686         }
6687         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6688                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6689                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6690                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6691         {
6692           ToggleSwitchgateSwitch(x, y + 1);
6693         }
6694         else if (smashed == EL_LIGHT_SWITCH ||
6695                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6696         {
6697           ToggleLightSwitch(x, y + 1);
6698         }
6699         else
6700         {
6701           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6702
6703           CheckElementChangeBySide(x, y + 1, smashed, element,
6704                                    CE_SWITCHED, CH_SIDE_TOP);
6705           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6706                                             CH_SIDE_TOP);
6707         }
6708       }
6709       else
6710       {
6711         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6712       }
6713     }
6714   }
6715
6716   // play sound of magic wall / mill
6717   if (!last_line &&
6718       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6719        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6720        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6721   {
6722     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6723       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6724     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6725       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6726     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6727       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6728
6729     return;
6730   }
6731
6732   // play sound of object that hits the ground
6733   if (last_line || object_hit)
6734     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6735 }
6736
6737 static void TurnRoundExt(int x, int y)
6738 {
6739   static struct
6740   {
6741     int dx, dy;
6742   } move_xy[] =
6743   {
6744     {  0,  0 },
6745     { -1,  0 },
6746     { +1,  0 },
6747     {  0,  0 },
6748     {  0, -1 },
6749     {  0,  0 }, { 0, 0 }, { 0, 0 },
6750     {  0, +1 }
6751   };
6752   static struct
6753   {
6754     int left, right, back;
6755   } turn[] =
6756   {
6757     { 0,        0,              0        },
6758     { MV_DOWN,  MV_UP,          MV_RIGHT },
6759     { MV_UP,    MV_DOWN,        MV_LEFT  },
6760     { 0,        0,              0        },
6761     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6762     { 0,        0,              0        },
6763     { 0,        0,              0        },
6764     { 0,        0,              0        },
6765     { MV_RIGHT, MV_LEFT,        MV_UP    }
6766   };
6767
6768   int element = Tile[x][y];
6769   int move_pattern = element_info[element].move_pattern;
6770
6771   int old_move_dir = MovDir[x][y];
6772   int left_dir  = turn[old_move_dir].left;
6773   int right_dir = turn[old_move_dir].right;
6774   int back_dir  = turn[old_move_dir].back;
6775
6776   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6777   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6778   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6779   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6780
6781   int left_x  = x + left_dx,  left_y  = y + left_dy;
6782   int right_x = x + right_dx, right_y = y + right_dy;
6783   int move_x  = x + move_dx,  move_y  = y + move_dy;
6784
6785   int xx, yy;
6786
6787   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6788   {
6789     TestIfBadThingTouchesOtherBadThing(x, y);
6790
6791     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6792       MovDir[x][y] = right_dir;
6793     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6794       MovDir[x][y] = left_dir;
6795
6796     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6797       MovDelay[x][y] = 9;
6798     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6799       MovDelay[x][y] = 1;
6800   }
6801   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6802   {
6803     TestIfBadThingTouchesOtherBadThing(x, y);
6804
6805     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6806       MovDir[x][y] = left_dir;
6807     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6808       MovDir[x][y] = right_dir;
6809
6810     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6811       MovDelay[x][y] = 9;
6812     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6813       MovDelay[x][y] = 1;
6814   }
6815   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6816   {
6817     TestIfBadThingTouchesOtherBadThing(x, y);
6818
6819     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6820       MovDir[x][y] = left_dir;
6821     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6822       MovDir[x][y] = right_dir;
6823
6824     if (MovDir[x][y] != old_move_dir)
6825       MovDelay[x][y] = 9;
6826   }
6827   else if (element == EL_YAMYAM)
6828   {
6829     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6830     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6831
6832     if (can_turn_left && can_turn_right)
6833       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6834     else if (can_turn_left)
6835       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6836     else if (can_turn_right)
6837       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6838     else
6839       MovDir[x][y] = back_dir;
6840
6841     MovDelay[x][y] = 16 + 16 * RND(3);
6842   }
6843   else if (element == EL_DARK_YAMYAM)
6844   {
6845     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6846                                                          left_x, left_y);
6847     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6848                                                          right_x, right_y);
6849
6850     if (can_turn_left && can_turn_right)
6851       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6852     else if (can_turn_left)
6853       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6854     else if (can_turn_right)
6855       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6856     else
6857       MovDir[x][y] = back_dir;
6858
6859     MovDelay[x][y] = 16 + 16 * RND(3);
6860   }
6861   else if (element == EL_PACMAN)
6862   {
6863     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6864     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6865
6866     if (can_turn_left && can_turn_right)
6867       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6868     else if (can_turn_left)
6869       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6870     else if (can_turn_right)
6871       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6872     else
6873       MovDir[x][y] = back_dir;
6874
6875     MovDelay[x][y] = 6 + RND(40);
6876   }
6877   else if (element == EL_PIG)
6878   {
6879     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6880     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6881     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6882     boolean should_turn_left, should_turn_right, should_move_on;
6883     int rnd_value = 24;
6884     int rnd = RND(rnd_value);
6885
6886     should_turn_left = (can_turn_left &&
6887                         (!can_move_on ||
6888                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6889                                                    y + back_dy + left_dy)));
6890     should_turn_right = (can_turn_right &&
6891                          (!can_move_on ||
6892                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6893                                                     y + back_dy + right_dy)));
6894     should_move_on = (can_move_on &&
6895                       (!can_turn_left ||
6896                        !can_turn_right ||
6897                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6898                                                  y + move_dy + left_dy) ||
6899                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6900                                                  y + move_dy + right_dy)));
6901
6902     if (should_turn_left || should_turn_right || should_move_on)
6903     {
6904       if (should_turn_left && should_turn_right && should_move_on)
6905         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6906                         rnd < 2 * rnd_value / 3 ? right_dir :
6907                         old_move_dir);
6908       else if (should_turn_left && should_turn_right)
6909         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6910       else if (should_turn_left && should_move_on)
6911         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6912       else if (should_turn_right && should_move_on)
6913         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6914       else if (should_turn_left)
6915         MovDir[x][y] = left_dir;
6916       else if (should_turn_right)
6917         MovDir[x][y] = right_dir;
6918       else if (should_move_on)
6919         MovDir[x][y] = old_move_dir;
6920     }
6921     else if (can_move_on && rnd > rnd_value / 8)
6922       MovDir[x][y] = old_move_dir;
6923     else if (can_turn_left && can_turn_right)
6924       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6925     else if (can_turn_left && rnd > rnd_value / 8)
6926       MovDir[x][y] = left_dir;
6927     else if (can_turn_right && rnd > rnd_value/8)
6928       MovDir[x][y] = right_dir;
6929     else
6930       MovDir[x][y] = back_dir;
6931
6932     xx = x + move_xy[MovDir[x][y]].dx;
6933     yy = y + move_xy[MovDir[x][y]].dy;
6934
6935     if (!IN_LEV_FIELD(xx, yy) ||
6936         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
6937       MovDir[x][y] = old_move_dir;
6938
6939     MovDelay[x][y] = 0;
6940   }
6941   else if (element == EL_DRAGON)
6942   {
6943     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6944     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6945     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6946     int rnd_value = 24;
6947     int rnd = RND(rnd_value);
6948
6949     if (can_move_on && rnd > rnd_value / 8)
6950       MovDir[x][y] = old_move_dir;
6951     else if (can_turn_left && can_turn_right)
6952       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6953     else if (can_turn_left && rnd > rnd_value / 8)
6954       MovDir[x][y] = left_dir;
6955     else if (can_turn_right && rnd > rnd_value / 8)
6956       MovDir[x][y] = right_dir;
6957     else
6958       MovDir[x][y] = back_dir;
6959
6960     xx = x + move_xy[MovDir[x][y]].dx;
6961     yy = y + move_xy[MovDir[x][y]].dy;
6962
6963     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6964       MovDir[x][y] = old_move_dir;
6965
6966     MovDelay[x][y] = 0;
6967   }
6968   else if (element == EL_MOLE)
6969   {
6970     boolean can_move_on =
6971       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6972                             IS_AMOEBOID(Tile[move_x][move_y]) ||
6973                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
6974     if (!can_move_on)
6975     {
6976       boolean can_turn_left =
6977         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6978                               IS_AMOEBOID(Tile[left_x][left_y])));
6979
6980       boolean can_turn_right =
6981         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6982                               IS_AMOEBOID(Tile[right_x][right_y])));
6983
6984       if (can_turn_left && can_turn_right)
6985         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6986       else if (can_turn_left)
6987         MovDir[x][y] = left_dir;
6988       else
6989         MovDir[x][y] = right_dir;
6990     }
6991
6992     if (MovDir[x][y] != old_move_dir)
6993       MovDelay[x][y] = 9;
6994   }
6995   else if (element == EL_BALLOON)
6996   {
6997     MovDir[x][y] = game.wind_direction;
6998     MovDelay[x][y] = 0;
6999   }
7000   else if (element == EL_SPRING)
7001   {
7002     if (MovDir[x][y] & MV_HORIZONTAL)
7003     {
7004       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7005           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7006       {
7007         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7008         ResetGfxAnimation(move_x, move_y);
7009         TEST_DrawLevelField(move_x, move_y);
7010
7011         MovDir[x][y] = back_dir;
7012       }
7013       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7014                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7015         MovDir[x][y] = MV_NONE;
7016     }
7017
7018     MovDelay[x][y] = 0;
7019   }
7020   else if (element == EL_ROBOT ||
7021            element == EL_SATELLITE ||
7022            element == EL_PENGUIN ||
7023            element == EL_EMC_ANDROID)
7024   {
7025     int attr_x = -1, attr_y = -1;
7026
7027     if (game.all_players_gone)
7028     {
7029       attr_x = game.exit_x;
7030       attr_y = game.exit_y;
7031     }
7032     else
7033     {
7034       int i;
7035
7036       for (i = 0; i < MAX_PLAYERS; i++)
7037       {
7038         struct PlayerInfo *player = &stored_player[i];
7039         int jx = player->jx, jy = player->jy;
7040
7041         if (!player->active)
7042           continue;
7043
7044         if (attr_x == -1 ||
7045             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7046         {
7047           attr_x = jx;
7048           attr_y = jy;
7049         }
7050       }
7051     }
7052
7053     if (element == EL_ROBOT &&
7054         game.robot_wheel_x >= 0 &&
7055         game.robot_wheel_y >= 0 &&
7056         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7057          game.engine_version < VERSION_IDENT(3,1,0,0)))
7058     {
7059       attr_x = game.robot_wheel_x;
7060       attr_y = game.robot_wheel_y;
7061     }
7062
7063     if (element == EL_PENGUIN)
7064     {
7065       int i;
7066       static int xy[4][2] =
7067       {
7068         { 0, -1 },
7069         { -1, 0 },
7070         { +1, 0 },
7071         { 0, +1 }
7072       };
7073
7074       for (i = 0; i < NUM_DIRECTIONS; i++)
7075       {
7076         int ex = x + xy[i][0];
7077         int ey = y + xy[i][1];
7078
7079         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7080                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7081                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7082                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7083         {
7084           attr_x = ex;
7085           attr_y = ey;
7086           break;
7087         }
7088       }
7089     }
7090
7091     MovDir[x][y] = MV_NONE;
7092     if (attr_x < x)
7093       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7094     else if (attr_x > x)
7095       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7096     if (attr_y < y)
7097       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7098     else if (attr_y > y)
7099       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7100
7101     if (element == EL_ROBOT)
7102     {
7103       int newx, newy;
7104
7105       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7106         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7107       Moving2Blocked(x, y, &newx, &newy);
7108
7109       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7110         MovDelay[x][y] = 8 + 8 * !RND(3);
7111       else
7112         MovDelay[x][y] = 16;
7113     }
7114     else if (element == EL_PENGUIN)
7115     {
7116       int newx, newy;
7117
7118       MovDelay[x][y] = 1;
7119
7120       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7121       {
7122         boolean first_horiz = RND(2);
7123         int new_move_dir = MovDir[x][y];
7124
7125         MovDir[x][y] =
7126           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7127         Moving2Blocked(x, y, &newx, &newy);
7128
7129         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7130           return;
7131
7132         MovDir[x][y] =
7133           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7134         Moving2Blocked(x, y, &newx, &newy);
7135
7136         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7137           return;
7138
7139         MovDir[x][y] = old_move_dir;
7140         return;
7141       }
7142     }
7143     else if (element == EL_SATELLITE)
7144     {
7145       int newx, newy;
7146
7147       MovDelay[x][y] = 1;
7148
7149       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7150       {
7151         boolean first_horiz = RND(2);
7152         int new_move_dir = MovDir[x][y];
7153
7154         MovDir[x][y] =
7155           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7156         Moving2Blocked(x, y, &newx, &newy);
7157
7158         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7159           return;
7160
7161         MovDir[x][y] =
7162           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7163         Moving2Blocked(x, y, &newx, &newy);
7164
7165         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7166           return;
7167
7168         MovDir[x][y] = old_move_dir;
7169         return;
7170       }
7171     }
7172     else if (element == EL_EMC_ANDROID)
7173     {
7174       static int check_pos[16] =
7175       {
7176         -1,             //  0 => (invalid)
7177         7,              //  1 => MV_LEFT
7178         3,              //  2 => MV_RIGHT
7179         -1,             //  3 => (invalid)
7180         1,              //  4 =>            MV_UP
7181         0,              //  5 => MV_LEFT  | MV_UP
7182         2,              //  6 => MV_RIGHT | MV_UP
7183         -1,             //  7 => (invalid)
7184         5,              //  8 =>            MV_DOWN
7185         6,              //  9 => MV_LEFT  | MV_DOWN
7186         4,              // 10 => MV_RIGHT | MV_DOWN
7187         -1,             // 11 => (invalid)
7188         -1,             // 12 => (invalid)
7189         -1,             // 13 => (invalid)
7190         -1,             // 14 => (invalid)
7191         -1,             // 15 => (invalid)
7192       };
7193       static struct
7194       {
7195         int dx, dy;
7196         int dir;
7197       } check_xy[8] =
7198       {
7199         { -1, -1,       MV_LEFT  | MV_UP   },
7200         {  0, -1,                  MV_UP   },
7201         { +1, -1,       MV_RIGHT | MV_UP   },
7202         { +1,  0,       MV_RIGHT           },
7203         { +1, +1,       MV_RIGHT | MV_DOWN },
7204         {  0, +1,                  MV_DOWN },
7205         { -1, +1,       MV_LEFT  | MV_DOWN },
7206         { -1,  0,       MV_LEFT            },
7207       };
7208       int start_pos, check_order;
7209       boolean can_clone = FALSE;
7210       int i;
7211
7212       // check if there is any free field around current position
7213       for (i = 0; i < 8; i++)
7214       {
7215         int newx = x + check_xy[i].dx;
7216         int newy = y + check_xy[i].dy;
7217
7218         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7219         {
7220           can_clone = TRUE;
7221
7222           break;
7223         }
7224       }
7225
7226       if (can_clone)            // randomly find an element to clone
7227       {
7228         can_clone = FALSE;
7229
7230         start_pos = check_pos[RND(8)];
7231         check_order = (RND(2) ? -1 : +1);
7232
7233         for (i = 0; i < 8; i++)
7234         {
7235           int pos_raw = start_pos + i * check_order;
7236           int pos = (pos_raw + 8) % 8;
7237           int newx = x + check_xy[pos].dx;
7238           int newy = y + check_xy[pos].dy;
7239
7240           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7241           {
7242             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7243             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7244
7245             Store[x][y] = Tile[newx][newy];
7246
7247             can_clone = TRUE;
7248
7249             break;
7250           }
7251         }
7252       }
7253
7254       if (can_clone)            // randomly find a direction to move
7255       {
7256         can_clone = FALSE;
7257
7258         start_pos = check_pos[RND(8)];
7259         check_order = (RND(2) ? -1 : +1);
7260
7261         for (i = 0; i < 8; i++)
7262         {
7263           int pos_raw = start_pos + i * check_order;
7264           int pos = (pos_raw + 8) % 8;
7265           int newx = x + check_xy[pos].dx;
7266           int newy = y + check_xy[pos].dy;
7267           int new_move_dir = check_xy[pos].dir;
7268
7269           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7270           {
7271             MovDir[x][y] = new_move_dir;
7272             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7273
7274             can_clone = TRUE;
7275
7276             break;
7277           }
7278         }
7279       }
7280
7281       if (can_clone)            // cloning and moving successful
7282         return;
7283
7284       // cannot clone -- try to move towards player
7285
7286       start_pos = check_pos[MovDir[x][y] & 0x0f];
7287       check_order = (RND(2) ? -1 : +1);
7288
7289       for (i = 0; i < 3; i++)
7290       {
7291         // first check start_pos, then previous/next or (next/previous) pos
7292         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7293         int pos = (pos_raw + 8) % 8;
7294         int newx = x + check_xy[pos].dx;
7295         int newy = y + check_xy[pos].dy;
7296         int new_move_dir = check_xy[pos].dir;
7297
7298         if (IS_PLAYER(newx, newy))
7299           break;
7300
7301         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7302         {
7303           MovDir[x][y] = new_move_dir;
7304           MovDelay[x][y] = level.android_move_time * 8 + 1;
7305
7306           break;
7307         }
7308       }
7309     }
7310   }
7311   else if (move_pattern == MV_TURNING_LEFT ||
7312            move_pattern == MV_TURNING_RIGHT ||
7313            move_pattern == MV_TURNING_LEFT_RIGHT ||
7314            move_pattern == MV_TURNING_RIGHT_LEFT ||
7315            move_pattern == MV_TURNING_RANDOM ||
7316            move_pattern == MV_ALL_DIRECTIONS)
7317   {
7318     boolean can_turn_left =
7319       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7320     boolean can_turn_right =
7321       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7322
7323     if (element_info[element].move_stepsize == 0)       // "not moving"
7324       return;
7325
7326     if (move_pattern == MV_TURNING_LEFT)
7327       MovDir[x][y] = left_dir;
7328     else if (move_pattern == MV_TURNING_RIGHT)
7329       MovDir[x][y] = right_dir;
7330     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7331       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7332     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7333       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7334     else if (move_pattern == MV_TURNING_RANDOM)
7335       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7336                       can_turn_right && !can_turn_left ? right_dir :
7337                       RND(2) ? left_dir : right_dir);
7338     else if (can_turn_left && can_turn_right)
7339       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7340     else if (can_turn_left)
7341       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7342     else if (can_turn_right)
7343       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7344     else
7345       MovDir[x][y] = back_dir;
7346
7347     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7348   }
7349   else if (move_pattern == MV_HORIZONTAL ||
7350            move_pattern == MV_VERTICAL)
7351   {
7352     if (move_pattern & old_move_dir)
7353       MovDir[x][y] = back_dir;
7354     else if (move_pattern == MV_HORIZONTAL)
7355       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7356     else if (move_pattern == MV_VERTICAL)
7357       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7358
7359     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7360   }
7361   else if (move_pattern & MV_ANY_DIRECTION)
7362   {
7363     MovDir[x][y] = move_pattern;
7364     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7365   }
7366   else if (move_pattern & MV_WIND_DIRECTION)
7367   {
7368     MovDir[x][y] = game.wind_direction;
7369     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7370   }
7371   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7372   {
7373     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7374       MovDir[x][y] = left_dir;
7375     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7376       MovDir[x][y] = right_dir;
7377
7378     if (MovDir[x][y] != old_move_dir)
7379       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7380   }
7381   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7382   {
7383     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7384       MovDir[x][y] = right_dir;
7385     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7386       MovDir[x][y] = left_dir;
7387
7388     if (MovDir[x][y] != old_move_dir)
7389       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7390   }
7391   else if (move_pattern == MV_TOWARDS_PLAYER ||
7392            move_pattern == MV_AWAY_FROM_PLAYER)
7393   {
7394     int attr_x = -1, attr_y = -1;
7395     int newx, newy;
7396     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7397
7398     if (game.all_players_gone)
7399     {
7400       attr_x = game.exit_x;
7401       attr_y = game.exit_y;
7402     }
7403     else
7404     {
7405       int i;
7406
7407       for (i = 0; i < MAX_PLAYERS; i++)
7408       {
7409         struct PlayerInfo *player = &stored_player[i];
7410         int jx = player->jx, jy = player->jy;
7411
7412         if (!player->active)
7413           continue;
7414
7415         if (attr_x == -1 ||
7416             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7417         {
7418           attr_x = jx;
7419           attr_y = jy;
7420         }
7421       }
7422     }
7423
7424     MovDir[x][y] = MV_NONE;
7425     if (attr_x < x)
7426       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7427     else if (attr_x > x)
7428       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7429     if (attr_y < y)
7430       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7431     else if (attr_y > y)
7432       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7433
7434     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7435
7436     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7437     {
7438       boolean first_horiz = RND(2);
7439       int new_move_dir = MovDir[x][y];
7440
7441       if (element_info[element].move_stepsize == 0)     // "not moving"
7442       {
7443         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7444         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7445
7446         return;
7447       }
7448
7449       MovDir[x][y] =
7450         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7451       Moving2Blocked(x, y, &newx, &newy);
7452
7453       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7454         return;
7455
7456       MovDir[x][y] =
7457         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7458       Moving2Blocked(x, y, &newx, &newy);
7459
7460       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7461         return;
7462
7463       MovDir[x][y] = old_move_dir;
7464     }
7465   }
7466   else if (move_pattern == MV_WHEN_PUSHED ||
7467            move_pattern == MV_WHEN_DROPPED)
7468   {
7469     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7470       MovDir[x][y] = MV_NONE;
7471
7472     MovDelay[x][y] = 0;
7473   }
7474   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7475   {
7476     static int test_xy[7][2] =
7477     {
7478       { 0, -1 },
7479       { -1, 0 },
7480       { +1, 0 },
7481       { 0, +1 },
7482       { 0, -1 },
7483       { -1, 0 },
7484       { +1, 0 },
7485     };
7486     static int test_dir[7] =
7487     {
7488       MV_UP,
7489       MV_LEFT,
7490       MV_RIGHT,
7491       MV_DOWN,
7492       MV_UP,
7493       MV_LEFT,
7494       MV_RIGHT,
7495     };
7496     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7497     int move_preference = -1000000;     // start with very low preference
7498     int new_move_dir = MV_NONE;
7499     int start_test = RND(4);
7500     int i;
7501
7502     for (i = 0; i < NUM_DIRECTIONS; i++)
7503     {
7504       int move_dir = test_dir[start_test + i];
7505       int move_dir_preference;
7506
7507       xx = x + test_xy[start_test + i][0];
7508       yy = y + test_xy[start_test + i][1];
7509
7510       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7511           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7512       {
7513         new_move_dir = move_dir;
7514
7515         break;
7516       }
7517
7518       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7519         continue;
7520
7521       move_dir_preference = -1 * RunnerVisit[xx][yy];
7522       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7523         move_dir_preference = PlayerVisit[xx][yy];
7524
7525       if (move_dir_preference > move_preference)
7526       {
7527         // prefer field that has not been visited for the longest time
7528         move_preference = move_dir_preference;
7529         new_move_dir = move_dir;
7530       }
7531       else if (move_dir_preference == move_preference &&
7532                move_dir == old_move_dir)
7533       {
7534         // prefer last direction when all directions are preferred equally
7535         move_preference = move_dir_preference;
7536         new_move_dir = move_dir;
7537       }
7538     }
7539
7540     MovDir[x][y] = new_move_dir;
7541     if (old_move_dir != new_move_dir)
7542       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7543   }
7544 }
7545
7546 static void TurnRound(int x, int y)
7547 {
7548   int direction = MovDir[x][y];
7549
7550   TurnRoundExt(x, y);
7551
7552   GfxDir[x][y] = MovDir[x][y];
7553
7554   if (direction != MovDir[x][y])
7555     GfxFrame[x][y] = 0;
7556
7557   if (MovDelay[x][y])
7558     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7559
7560   ResetGfxFrame(x, y);
7561 }
7562
7563 static boolean JustBeingPushed(int x, int y)
7564 {
7565   int i;
7566
7567   for (i = 0; i < MAX_PLAYERS; i++)
7568   {
7569     struct PlayerInfo *player = &stored_player[i];
7570
7571     if (player->active && player->is_pushing && player->MovPos)
7572     {
7573       int next_jx = player->jx + (player->jx - player->last_jx);
7574       int next_jy = player->jy + (player->jy - player->last_jy);
7575
7576       if (x == next_jx && y == next_jy)
7577         return TRUE;
7578     }
7579   }
7580
7581   return FALSE;
7582 }
7583
7584 static void StartMoving(int x, int y)
7585 {
7586   boolean started_moving = FALSE;       // some elements can fall _and_ move
7587   int element = Tile[x][y];
7588
7589   if (Stop[x][y])
7590     return;
7591
7592   if (MovDelay[x][y] == 0)
7593     GfxAction[x][y] = ACTION_DEFAULT;
7594
7595   if (CAN_FALL(element) && y < lev_fieldy - 1)
7596   {
7597     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7598         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7599       if (JustBeingPushed(x, y))
7600         return;
7601
7602     if (element == EL_QUICKSAND_FULL)
7603     {
7604       if (IS_FREE(x, y + 1))
7605       {
7606         InitMovingField(x, y, MV_DOWN);
7607         started_moving = TRUE;
7608
7609         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7610 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7611         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7612           Store[x][y] = EL_ROCK;
7613 #else
7614         Store[x][y] = EL_ROCK;
7615 #endif
7616
7617         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7618       }
7619       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7620       {
7621         if (!MovDelay[x][y])
7622         {
7623           MovDelay[x][y] = TILEY + 1;
7624
7625           ResetGfxAnimation(x, y);
7626           ResetGfxAnimation(x, y + 1);
7627         }
7628
7629         if (MovDelay[x][y])
7630         {
7631           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7632           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7633
7634           MovDelay[x][y]--;
7635           if (MovDelay[x][y])
7636             return;
7637         }
7638
7639         Tile[x][y] = EL_QUICKSAND_EMPTY;
7640         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7641         Store[x][y + 1] = Store[x][y];
7642         Store[x][y] = 0;
7643
7644         PlayLevelSoundAction(x, y, ACTION_FILLING);
7645       }
7646       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7647       {
7648         if (!MovDelay[x][y])
7649         {
7650           MovDelay[x][y] = TILEY + 1;
7651
7652           ResetGfxAnimation(x, y);
7653           ResetGfxAnimation(x, y + 1);
7654         }
7655
7656         if (MovDelay[x][y])
7657         {
7658           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7659           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7660
7661           MovDelay[x][y]--;
7662           if (MovDelay[x][y])
7663             return;
7664         }
7665
7666         Tile[x][y] = EL_QUICKSAND_EMPTY;
7667         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7668         Store[x][y + 1] = Store[x][y];
7669         Store[x][y] = 0;
7670
7671         PlayLevelSoundAction(x, y, ACTION_FILLING);
7672       }
7673     }
7674     else if (element == EL_QUICKSAND_FAST_FULL)
7675     {
7676       if (IS_FREE(x, y + 1))
7677       {
7678         InitMovingField(x, y, MV_DOWN);
7679         started_moving = TRUE;
7680
7681         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7682 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7683         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7684           Store[x][y] = EL_ROCK;
7685 #else
7686         Store[x][y] = EL_ROCK;
7687 #endif
7688
7689         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7690       }
7691       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7692       {
7693         if (!MovDelay[x][y])
7694         {
7695           MovDelay[x][y] = TILEY + 1;
7696
7697           ResetGfxAnimation(x, y);
7698           ResetGfxAnimation(x, y + 1);
7699         }
7700
7701         if (MovDelay[x][y])
7702         {
7703           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7704           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7705
7706           MovDelay[x][y]--;
7707           if (MovDelay[x][y])
7708             return;
7709         }
7710
7711         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7712         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7713         Store[x][y + 1] = Store[x][y];
7714         Store[x][y] = 0;
7715
7716         PlayLevelSoundAction(x, y, ACTION_FILLING);
7717       }
7718       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7719       {
7720         if (!MovDelay[x][y])
7721         {
7722           MovDelay[x][y] = TILEY + 1;
7723
7724           ResetGfxAnimation(x, y);
7725           ResetGfxAnimation(x, y + 1);
7726         }
7727
7728         if (MovDelay[x][y])
7729         {
7730           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7731           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7732
7733           MovDelay[x][y]--;
7734           if (MovDelay[x][y])
7735             return;
7736         }
7737
7738         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7739         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7740         Store[x][y + 1] = Store[x][y];
7741         Store[x][y] = 0;
7742
7743         PlayLevelSoundAction(x, y, ACTION_FILLING);
7744       }
7745     }
7746     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7747              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7748     {
7749       InitMovingField(x, y, MV_DOWN);
7750       started_moving = TRUE;
7751
7752       Tile[x][y] = EL_QUICKSAND_FILLING;
7753       Store[x][y] = element;
7754
7755       PlayLevelSoundAction(x, y, ACTION_FILLING);
7756     }
7757     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7758              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7759     {
7760       InitMovingField(x, y, MV_DOWN);
7761       started_moving = TRUE;
7762
7763       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7764       Store[x][y] = element;
7765
7766       PlayLevelSoundAction(x, y, ACTION_FILLING);
7767     }
7768     else if (element == EL_MAGIC_WALL_FULL)
7769     {
7770       if (IS_FREE(x, y + 1))
7771       {
7772         InitMovingField(x, y, MV_DOWN);
7773         started_moving = TRUE;
7774
7775         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7776         Store[x][y] = EL_CHANGED(Store[x][y]);
7777       }
7778       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7779       {
7780         if (!MovDelay[x][y])
7781           MovDelay[x][y] = TILEY / 4 + 1;
7782
7783         if (MovDelay[x][y])
7784         {
7785           MovDelay[x][y]--;
7786           if (MovDelay[x][y])
7787             return;
7788         }
7789
7790         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7791         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7792         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7793         Store[x][y] = 0;
7794       }
7795     }
7796     else if (element == EL_BD_MAGIC_WALL_FULL)
7797     {
7798       if (IS_FREE(x, y + 1))
7799       {
7800         InitMovingField(x, y, MV_DOWN);
7801         started_moving = TRUE;
7802
7803         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7804         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7805       }
7806       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7807       {
7808         if (!MovDelay[x][y])
7809           MovDelay[x][y] = TILEY / 4 + 1;
7810
7811         if (MovDelay[x][y])
7812         {
7813           MovDelay[x][y]--;
7814           if (MovDelay[x][y])
7815             return;
7816         }
7817
7818         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7819         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7820         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7821         Store[x][y] = 0;
7822       }
7823     }
7824     else if (element == EL_DC_MAGIC_WALL_FULL)
7825     {
7826       if (IS_FREE(x, y + 1))
7827       {
7828         InitMovingField(x, y, MV_DOWN);
7829         started_moving = TRUE;
7830
7831         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7832         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7833       }
7834       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7835       {
7836         if (!MovDelay[x][y])
7837           MovDelay[x][y] = TILEY / 4 + 1;
7838
7839         if (MovDelay[x][y])
7840         {
7841           MovDelay[x][y]--;
7842           if (MovDelay[x][y])
7843             return;
7844         }
7845
7846         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7847         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7848         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7849         Store[x][y] = 0;
7850       }
7851     }
7852     else if ((CAN_PASS_MAGIC_WALL(element) &&
7853               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7854                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7855              (CAN_PASS_DC_MAGIC_WALL(element) &&
7856               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7857
7858     {
7859       InitMovingField(x, y, MV_DOWN);
7860       started_moving = TRUE;
7861
7862       Tile[x][y] =
7863         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7864          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7865          EL_DC_MAGIC_WALL_FILLING);
7866       Store[x][y] = element;
7867     }
7868     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
7869     {
7870       SplashAcid(x, y + 1);
7871
7872       InitMovingField(x, y, MV_DOWN);
7873       started_moving = TRUE;
7874
7875       Store[x][y] = EL_ACID;
7876     }
7877     else if (
7878              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7879               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7880              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7881               CAN_FALL(element) && WasJustFalling[x][y] &&
7882               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7883
7884              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7885               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7886               (Tile[x][y + 1] == EL_BLOCKED)))
7887     {
7888       /* this is needed for a special case not covered by calling "Impact()"
7889          from "ContinueMoving()": if an element moves to a tile directly below
7890          another element which was just falling on that tile (which was empty
7891          in the previous frame), the falling element above would just stop
7892          instead of smashing the element below (in previous version, the above
7893          element was just checked for "moving" instead of "falling", resulting
7894          in incorrect smashes caused by horizontal movement of the above
7895          element; also, the case of the player being the element to smash was
7896          simply not covered here... :-/ ) */
7897
7898       CheckCollision[x][y] = 0;
7899       CheckImpact[x][y] = 0;
7900
7901       Impact(x, y);
7902     }
7903     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7904     {
7905       if (MovDir[x][y] == MV_NONE)
7906       {
7907         InitMovingField(x, y, MV_DOWN);
7908         started_moving = TRUE;
7909       }
7910     }
7911     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
7912     {
7913       if (WasJustFalling[x][y]) // prevent animation from being restarted
7914         MovDir[x][y] = MV_DOWN;
7915
7916       InitMovingField(x, y, MV_DOWN);
7917       started_moving = TRUE;
7918     }
7919     else if (element == EL_AMOEBA_DROP)
7920     {
7921       Tile[x][y] = EL_AMOEBA_GROWING;
7922       Store[x][y] = EL_AMOEBA_WET;
7923     }
7924     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7925               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
7926              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7927              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7928     {
7929       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7930                                 (IS_FREE(x - 1, y + 1) ||
7931                                  Tile[x - 1][y + 1] == EL_ACID));
7932       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7933                                 (IS_FREE(x + 1, y + 1) ||
7934                                  Tile[x + 1][y + 1] == EL_ACID));
7935       boolean can_fall_any  = (can_fall_left || can_fall_right);
7936       boolean can_fall_both = (can_fall_left && can_fall_right);
7937       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
7938
7939       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7940       {
7941         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7942           can_fall_right = FALSE;
7943         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7944           can_fall_left = FALSE;
7945         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7946           can_fall_right = FALSE;
7947         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7948           can_fall_left = FALSE;
7949
7950         can_fall_any  = (can_fall_left || can_fall_right);
7951         can_fall_both = FALSE;
7952       }
7953
7954       if (can_fall_both)
7955       {
7956         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7957           can_fall_right = FALSE;       // slip down on left side
7958         else
7959           can_fall_left = !(can_fall_right = RND(2));
7960
7961         can_fall_both = FALSE;
7962       }
7963
7964       if (can_fall_any)
7965       {
7966         // if not determined otherwise, prefer left side for slipping down
7967         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7968         started_moving = TRUE;
7969       }
7970     }
7971     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
7972     {
7973       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7974       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7975       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
7976       int belt_dir = game.belt_dir[belt_nr];
7977
7978       if ((belt_dir == MV_LEFT  && left_is_free) ||
7979           (belt_dir == MV_RIGHT && right_is_free))
7980       {
7981         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7982
7983         InitMovingField(x, y, belt_dir);
7984         started_moving = TRUE;
7985
7986         Pushed[x][y] = TRUE;
7987         Pushed[nextx][y] = TRUE;
7988
7989         GfxAction[x][y] = ACTION_DEFAULT;
7990       }
7991       else
7992       {
7993         MovDir[x][y] = 0;       // if element was moving, stop it
7994       }
7995     }
7996   }
7997
7998   // not "else if" because of elements that can fall and move (EL_SPRING)
7999   if (CAN_MOVE(element) && !started_moving)
8000   {
8001     int move_pattern = element_info[element].move_pattern;
8002     int newx, newy;
8003
8004     Moving2Blocked(x, y, &newx, &newy);
8005
8006     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8007       return;
8008
8009     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8010         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8011     {
8012       WasJustMoving[x][y] = 0;
8013       CheckCollision[x][y] = 0;
8014
8015       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8016
8017       if (Tile[x][y] != element)        // element has changed
8018         return;
8019     }
8020
8021     if (!MovDelay[x][y])        // start new movement phase
8022     {
8023       // all objects that can change their move direction after each step
8024       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8025
8026       if (element != EL_YAMYAM &&
8027           element != EL_DARK_YAMYAM &&
8028           element != EL_PACMAN &&
8029           !(move_pattern & MV_ANY_DIRECTION) &&
8030           move_pattern != MV_TURNING_LEFT &&
8031           move_pattern != MV_TURNING_RIGHT &&
8032           move_pattern != MV_TURNING_LEFT_RIGHT &&
8033           move_pattern != MV_TURNING_RIGHT_LEFT &&
8034           move_pattern != MV_TURNING_RANDOM)
8035       {
8036         TurnRound(x, y);
8037
8038         if (MovDelay[x][y] && (element == EL_BUG ||
8039                                element == EL_SPACESHIP ||
8040                                element == EL_SP_SNIKSNAK ||
8041                                element == EL_SP_ELECTRON ||
8042                                element == EL_MOLE))
8043           TEST_DrawLevelField(x, y);
8044       }
8045     }
8046
8047     if (MovDelay[x][y])         // wait some time before next movement
8048     {
8049       MovDelay[x][y]--;
8050
8051       if (element == EL_ROBOT ||
8052           element == EL_YAMYAM ||
8053           element == EL_DARK_YAMYAM)
8054       {
8055         DrawLevelElementAnimationIfNeeded(x, y, element);
8056         PlayLevelSoundAction(x, y, ACTION_WAITING);
8057       }
8058       else if (element == EL_SP_ELECTRON)
8059         DrawLevelElementAnimationIfNeeded(x, y, element);
8060       else if (element == EL_DRAGON)
8061       {
8062         int i;
8063         int dir = MovDir[x][y];
8064         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8065         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8066         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8067                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8068                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8069                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8070         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8071
8072         GfxAction[x][y] = ACTION_ATTACKING;
8073
8074         if (IS_PLAYER(x, y))
8075           DrawPlayerField(x, y);
8076         else
8077           TEST_DrawLevelField(x, y);
8078
8079         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8080
8081         for (i = 1; i <= 3; i++)
8082         {
8083           int xx = x + i * dx;
8084           int yy = y + i * dy;
8085           int sx = SCREENX(xx);
8086           int sy = SCREENY(yy);
8087           int flame_graphic = graphic + (i - 1);
8088
8089           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8090             break;
8091
8092           if (MovDelay[x][y])
8093           {
8094             int flamed = MovingOrBlocked2Element(xx, yy);
8095
8096             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8097               Bang(xx, yy);
8098             else
8099               RemoveMovingField(xx, yy);
8100
8101             ChangeDelay[xx][yy] = 0;
8102
8103             Tile[xx][yy] = EL_FLAMES;
8104
8105             if (IN_SCR_FIELD(sx, sy))
8106             {
8107               TEST_DrawLevelFieldCrumbled(xx, yy);
8108               DrawGraphic(sx, sy, flame_graphic, frame);
8109             }
8110           }
8111           else
8112           {
8113             if (Tile[xx][yy] == EL_FLAMES)
8114               Tile[xx][yy] = EL_EMPTY;
8115             TEST_DrawLevelField(xx, yy);
8116           }
8117         }
8118       }
8119
8120       if (MovDelay[x][y])       // element still has to wait some time
8121       {
8122         PlayLevelSoundAction(x, y, ACTION_WAITING);
8123
8124         return;
8125       }
8126     }
8127
8128     // now make next step
8129
8130     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8131
8132     if (DONT_COLLIDE_WITH(element) &&
8133         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8134         !PLAYER_ENEMY_PROTECTED(newx, newy))
8135     {
8136       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8137
8138       return;
8139     }
8140
8141     else if (CAN_MOVE_INTO_ACID(element) &&
8142              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8143              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8144              (MovDir[x][y] == MV_DOWN ||
8145               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8146     {
8147       SplashAcid(newx, newy);
8148       Store[x][y] = EL_ACID;
8149     }
8150     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8151     {
8152       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8153           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8154           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8155           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8156       {
8157         RemoveField(x, y);
8158         TEST_DrawLevelField(x, y);
8159
8160         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8161         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8162           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8163
8164         game.friends_still_needed--;
8165         if (!game.friends_still_needed &&
8166             !game.GameOver &&
8167             game.all_players_gone)
8168           LevelSolved();
8169
8170         return;
8171       }
8172       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8173       {
8174         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8175           TEST_DrawLevelField(newx, newy);
8176         else
8177           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8178       }
8179       else if (!IS_FREE(newx, newy))
8180       {
8181         GfxAction[x][y] = ACTION_WAITING;
8182
8183         if (IS_PLAYER(x, y))
8184           DrawPlayerField(x, y);
8185         else
8186           TEST_DrawLevelField(x, y);
8187
8188         return;
8189       }
8190     }
8191     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8192     {
8193       if (IS_FOOD_PIG(Tile[newx][newy]))
8194       {
8195         if (IS_MOVING(newx, newy))
8196           RemoveMovingField(newx, newy);
8197         else
8198         {
8199           Tile[newx][newy] = EL_EMPTY;
8200           TEST_DrawLevelField(newx, newy);
8201         }
8202
8203         PlayLevelSound(x, y, SND_PIG_DIGGING);
8204       }
8205       else if (!IS_FREE(newx, newy))
8206       {
8207         if (IS_PLAYER(x, y))
8208           DrawPlayerField(x, y);
8209         else
8210           TEST_DrawLevelField(x, y);
8211
8212         return;
8213       }
8214     }
8215     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8216     {
8217       if (Store[x][y] != EL_EMPTY)
8218       {
8219         boolean can_clone = FALSE;
8220         int xx, yy;
8221
8222         // check if element to clone is still there
8223         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8224         {
8225           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8226           {
8227             can_clone = TRUE;
8228
8229             break;
8230           }
8231         }
8232
8233         // cannot clone or target field not free anymore -- do not clone
8234         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8235           Store[x][y] = EL_EMPTY;
8236       }
8237
8238       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8239       {
8240         if (IS_MV_DIAGONAL(MovDir[x][y]))
8241         {
8242           int diagonal_move_dir = MovDir[x][y];
8243           int stored = Store[x][y];
8244           int change_delay = 8;
8245           int graphic;
8246
8247           // android is moving diagonally
8248
8249           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8250
8251           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8252           GfxElement[x][y] = EL_EMC_ANDROID;
8253           GfxAction[x][y] = ACTION_SHRINKING;
8254           GfxDir[x][y] = diagonal_move_dir;
8255           ChangeDelay[x][y] = change_delay;
8256
8257           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8258                                    GfxDir[x][y]);
8259
8260           DrawLevelGraphicAnimation(x, y, graphic);
8261           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8262
8263           if (Tile[newx][newy] == EL_ACID)
8264           {
8265             SplashAcid(newx, newy);
8266
8267             return;
8268           }
8269
8270           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8271
8272           Store[newx][newy] = EL_EMC_ANDROID;
8273           GfxElement[newx][newy] = EL_EMC_ANDROID;
8274           GfxAction[newx][newy] = ACTION_GROWING;
8275           GfxDir[newx][newy] = diagonal_move_dir;
8276           ChangeDelay[newx][newy] = change_delay;
8277
8278           graphic = el_act_dir2img(GfxElement[newx][newy],
8279                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8280
8281           DrawLevelGraphicAnimation(newx, newy, graphic);
8282           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8283
8284           return;
8285         }
8286         else
8287         {
8288           Tile[newx][newy] = EL_EMPTY;
8289           TEST_DrawLevelField(newx, newy);
8290
8291           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8292         }
8293       }
8294       else if (!IS_FREE(newx, newy))
8295       {
8296         return;
8297       }
8298     }
8299     else if (IS_CUSTOM_ELEMENT(element) &&
8300              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8301     {
8302       if (!DigFieldByCE(newx, newy, element))
8303         return;
8304
8305       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8306       {
8307         RunnerVisit[x][y] = FrameCounter;
8308         PlayerVisit[x][y] /= 8;         // expire player visit path
8309       }
8310     }
8311     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8312     {
8313       if (!IS_FREE(newx, newy))
8314       {
8315         if (IS_PLAYER(x, y))
8316           DrawPlayerField(x, y);
8317         else
8318           TEST_DrawLevelField(x, y);
8319
8320         return;
8321       }
8322       else
8323       {
8324         boolean wanna_flame = !RND(10);
8325         int dx = newx - x, dy = newy - y;
8326         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8327         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8328         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8329                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8330         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8331                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8332
8333         if ((wanna_flame ||
8334              IS_CLASSIC_ENEMY(element1) ||
8335              IS_CLASSIC_ENEMY(element2)) &&
8336             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8337             element1 != EL_FLAMES && element2 != EL_FLAMES)
8338         {
8339           ResetGfxAnimation(x, y);
8340           GfxAction[x][y] = ACTION_ATTACKING;
8341
8342           if (IS_PLAYER(x, y))
8343             DrawPlayerField(x, y);
8344           else
8345             TEST_DrawLevelField(x, y);
8346
8347           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8348
8349           MovDelay[x][y] = 50;
8350
8351           Tile[newx][newy] = EL_FLAMES;
8352           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8353             Tile[newx1][newy1] = EL_FLAMES;
8354           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8355             Tile[newx2][newy2] = EL_FLAMES;
8356
8357           return;
8358         }
8359       }
8360     }
8361     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8362              Tile[newx][newy] == EL_DIAMOND)
8363     {
8364       if (IS_MOVING(newx, newy))
8365         RemoveMovingField(newx, newy);
8366       else
8367       {
8368         Tile[newx][newy] = EL_EMPTY;
8369         TEST_DrawLevelField(newx, newy);
8370       }
8371
8372       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8373     }
8374     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8375              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8376     {
8377       if (AmoebaNr[newx][newy])
8378       {
8379         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8380         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8381             Tile[newx][newy] == EL_BD_AMOEBA)
8382           AmoebaCnt[AmoebaNr[newx][newy]]--;
8383       }
8384
8385       if (IS_MOVING(newx, newy))
8386       {
8387         RemoveMovingField(newx, newy);
8388       }
8389       else
8390       {
8391         Tile[newx][newy] = EL_EMPTY;
8392         TEST_DrawLevelField(newx, newy);
8393       }
8394
8395       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8396     }
8397     else if ((element == EL_PACMAN || element == EL_MOLE)
8398              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8399     {
8400       if (AmoebaNr[newx][newy])
8401       {
8402         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8403         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8404             Tile[newx][newy] == EL_BD_AMOEBA)
8405           AmoebaCnt[AmoebaNr[newx][newy]]--;
8406       }
8407
8408       if (element == EL_MOLE)
8409       {
8410         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8411         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8412
8413         ResetGfxAnimation(x, y);
8414         GfxAction[x][y] = ACTION_DIGGING;
8415         TEST_DrawLevelField(x, y);
8416
8417         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8418
8419         return;                         // wait for shrinking amoeba
8420       }
8421       else      // element == EL_PACMAN
8422       {
8423         Tile[newx][newy] = EL_EMPTY;
8424         TEST_DrawLevelField(newx, newy);
8425         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8426       }
8427     }
8428     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8429              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8430               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8431     {
8432       // wait for shrinking amoeba to completely disappear
8433       return;
8434     }
8435     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8436     {
8437       // object was running against a wall
8438
8439       TurnRound(x, y);
8440
8441       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8442         DrawLevelElementAnimation(x, y, element);
8443
8444       if (DONT_TOUCH(element))
8445         TestIfBadThingTouchesPlayer(x, y);
8446
8447       return;
8448     }
8449
8450     InitMovingField(x, y, MovDir[x][y]);
8451
8452     PlayLevelSoundAction(x, y, ACTION_MOVING);
8453   }
8454
8455   if (MovDir[x][y])
8456     ContinueMoving(x, y);
8457 }
8458
8459 void ContinueMoving(int x, int y)
8460 {
8461   int element = Tile[x][y];
8462   struct ElementInfo *ei = &element_info[element];
8463   int direction = MovDir[x][y];
8464   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8465   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8466   int newx = x + dx, newy = y + dy;
8467   int stored = Store[x][y];
8468   int stored_new = Store[newx][newy];
8469   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8470   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8471   boolean last_line = (newy == lev_fieldy - 1);
8472
8473   MovPos[x][y] += getElementMoveStepsize(x, y);
8474
8475   if (pushed_by_player) // special case: moving object pushed by player
8476     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8477
8478   if (ABS(MovPos[x][y]) < TILEX)
8479   {
8480     TEST_DrawLevelField(x, y);
8481
8482     return;     // element is still moving
8483   }
8484
8485   // element reached destination field
8486
8487   Tile[x][y] = EL_EMPTY;
8488   Tile[newx][newy] = element;
8489   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8490
8491   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8492   {
8493     element = Tile[newx][newy] = EL_ACID;
8494   }
8495   else if (element == EL_MOLE)
8496   {
8497     Tile[x][y] = EL_SAND;
8498
8499     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8500   }
8501   else if (element == EL_QUICKSAND_FILLING)
8502   {
8503     element = Tile[newx][newy] = get_next_element(element);
8504     Store[newx][newy] = Store[x][y];
8505   }
8506   else if (element == EL_QUICKSAND_EMPTYING)
8507   {
8508     Tile[x][y] = get_next_element(element);
8509     element = Tile[newx][newy] = Store[x][y];
8510   }
8511   else if (element == EL_QUICKSAND_FAST_FILLING)
8512   {
8513     element = Tile[newx][newy] = get_next_element(element);
8514     Store[newx][newy] = Store[x][y];
8515   }
8516   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8517   {
8518     Tile[x][y] = get_next_element(element);
8519     element = Tile[newx][newy] = Store[x][y];
8520   }
8521   else if (element == EL_MAGIC_WALL_FILLING)
8522   {
8523     element = Tile[newx][newy] = get_next_element(element);
8524     if (!game.magic_wall_active)
8525       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8526     Store[newx][newy] = Store[x][y];
8527   }
8528   else if (element == EL_MAGIC_WALL_EMPTYING)
8529   {
8530     Tile[x][y] = get_next_element(element);
8531     if (!game.magic_wall_active)
8532       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8533     element = Tile[newx][newy] = Store[x][y];
8534
8535     InitField(newx, newy, FALSE);
8536   }
8537   else if (element == EL_BD_MAGIC_WALL_FILLING)
8538   {
8539     element = Tile[newx][newy] = get_next_element(element);
8540     if (!game.magic_wall_active)
8541       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8542     Store[newx][newy] = Store[x][y];
8543   }
8544   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8545   {
8546     Tile[x][y] = get_next_element(element);
8547     if (!game.magic_wall_active)
8548       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8549     element = Tile[newx][newy] = Store[x][y];
8550
8551     InitField(newx, newy, FALSE);
8552   }
8553   else if (element == EL_DC_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_DC_MAGIC_WALL_DEAD;
8558     Store[newx][newy] = Store[x][y];
8559   }
8560   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8561   {
8562     Tile[x][y] = get_next_element(element);
8563     if (!game.magic_wall_active)
8564       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8565     element = Tile[newx][newy] = Store[x][y];
8566
8567     InitField(newx, newy, FALSE);
8568   }
8569   else if (element == EL_AMOEBA_DROPPING)
8570   {
8571     Tile[x][y] = get_next_element(element);
8572     element = Tile[newx][newy] = Store[x][y];
8573   }
8574   else if (element == EL_SOKOBAN_OBJECT)
8575   {
8576     if (Back[x][y])
8577       Tile[x][y] = Back[x][y];
8578
8579     if (Back[newx][newy])
8580       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8581
8582     Back[x][y] = Back[newx][newy] = 0;
8583   }
8584
8585   Store[x][y] = EL_EMPTY;
8586   MovPos[x][y] = 0;
8587   MovDir[x][y] = 0;
8588   MovDelay[x][y] = 0;
8589
8590   MovDelay[newx][newy] = 0;
8591
8592   if (CAN_CHANGE_OR_HAS_ACTION(element))
8593   {
8594     // copy element change control values to new field
8595     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8596     ChangePage[newx][newy]  = ChangePage[x][y];
8597     ChangeCount[newx][newy] = ChangeCount[x][y];
8598     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8599   }
8600
8601   CustomValue[newx][newy] = CustomValue[x][y];
8602
8603   ChangeDelay[x][y] = 0;
8604   ChangePage[x][y] = -1;
8605   ChangeCount[x][y] = 0;
8606   ChangeEvent[x][y] = -1;
8607
8608   CustomValue[x][y] = 0;
8609
8610   // copy animation control values to new field
8611   GfxFrame[newx][newy]  = GfxFrame[x][y];
8612   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8613   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8614   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8615
8616   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8617
8618   // some elements can leave other elements behind after moving
8619   if (ei->move_leave_element != EL_EMPTY &&
8620       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8621       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8622   {
8623     int move_leave_element = ei->move_leave_element;
8624
8625     // this makes it possible to leave the removed element again
8626     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8627       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8628
8629     Tile[x][y] = move_leave_element;
8630
8631     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8632       MovDir[x][y] = direction;
8633
8634     InitField(x, y, FALSE);
8635
8636     if (GFX_CRUMBLED(Tile[x][y]))
8637       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8638
8639     if (ELEM_IS_PLAYER(move_leave_element))
8640       RelocatePlayer(x, y, move_leave_element);
8641   }
8642
8643   // do this after checking for left-behind element
8644   ResetGfxAnimation(x, y);      // reset animation values for old field
8645
8646   if (!CAN_MOVE(element) ||
8647       (CAN_FALL(element) && direction == MV_DOWN &&
8648        (element == EL_SPRING ||
8649         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8650         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8651     GfxDir[x][y] = MovDir[newx][newy] = 0;
8652
8653   TEST_DrawLevelField(x, y);
8654   TEST_DrawLevelField(newx, newy);
8655
8656   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8657
8658   // prevent pushed element from moving on in pushed direction
8659   if (pushed_by_player && CAN_MOVE(element) &&
8660       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8661       !(element_info[element].move_pattern & direction))
8662     TurnRound(newx, newy);
8663
8664   // prevent elements on conveyor belt from moving on in last direction
8665   if (pushed_by_conveyor && CAN_FALL(element) &&
8666       direction & MV_HORIZONTAL)
8667     MovDir[newx][newy] = 0;
8668
8669   if (!pushed_by_player)
8670   {
8671     int nextx = newx + dx, nexty = newy + dy;
8672     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8673
8674     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8675
8676     if (CAN_FALL(element) && direction == MV_DOWN)
8677       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8678
8679     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8680       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8681
8682     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8683       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8684   }
8685
8686   if (DONT_TOUCH(element))      // object may be nasty to player or others
8687   {
8688     TestIfBadThingTouchesPlayer(newx, newy);
8689     TestIfBadThingTouchesFriend(newx, newy);
8690
8691     if (!IS_CUSTOM_ELEMENT(element))
8692       TestIfBadThingTouchesOtherBadThing(newx, newy);
8693   }
8694   else if (element == EL_PENGUIN)
8695     TestIfFriendTouchesBadThing(newx, newy);
8696
8697   if (DONT_GET_HIT_BY(element))
8698   {
8699     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8700   }
8701
8702   // give the player one last chance (one more frame) to move away
8703   if (CAN_FALL(element) && direction == MV_DOWN &&
8704       (last_line || (!IS_FREE(x, newy + 1) &&
8705                      (!IS_PLAYER(x, newy + 1) ||
8706                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8707     Impact(x, newy);
8708
8709   if (pushed_by_player && !game.use_change_when_pushing_bug)
8710   {
8711     int push_side = MV_DIR_OPPOSITE(direction);
8712     struct PlayerInfo *player = PLAYERINFO(x, y);
8713
8714     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8715                                player->index_bit, push_side);
8716     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8717                                         player->index_bit, push_side);
8718   }
8719
8720   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8721     MovDelay[newx][newy] = 1;
8722
8723   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8724
8725   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8726   TestIfElementHitsCustomElement(newx, newy, direction);
8727   TestIfPlayerTouchesCustomElement(newx, newy);
8728   TestIfElementTouchesCustomElement(newx, newy);
8729
8730   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8731       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8732     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8733                              MV_DIR_OPPOSITE(direction));
8734 }
8735
8736 int AmoebaNeighbourNr(int ax, int ay)
8737 {
8738   int i;
8739   int element = Tile[ax][ay];
8740   int group_nr = 0;
8741   static int xy[4][2] =
8742   {
8743     { 0, -1 },
8744     { -1, 0 },
8745     { +1, 0 },
8746     { 0, +1 }
8747   };
8748
8749   for (i = 0; i < NUM_DIRECTIONS; i++)
8750   {
8751     int x = ax + xy[i][0];
8752     int y = ay + xy[i][1];
8753
8754     if (!IN_LEV_FIELD(x, y))
8755       continue;
8756
8757     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8758       group_nr = AmoebaNr[x][y];
8759   }
8760
8761   return group_nr;
8762 }
8763
8764 static void AmoebaMerge(int ax, int ay)
8765 {
8766   int i, x, y, xx, yy;
8767   int new_group_nr = AmoebaNr[ax][ay];
8768   static int xy[4][2] =
8769   {
8770     { 0, -1 },
8771     { -1, 0 },
8772     { +1, 0 },
8773     { 0, +1 }
8774   };
8775
8776   if (new_group_nr == 0)
8777     return;
8778
8779   for (i = 0; i < NUM_DIRECTIONS; i++)
8780   {
8781     x = ax + xy[i][0];
8782     y = ay + xy[i][1];
8783
8784     if (!IN_LEV_FIELD(x, y))
8785       continue;
8786
8787     if ((Tile[x][y] == EL_AMOEBA_FULL ||
8788          Tile[x][y] == EL_BD_AMOEBA ||
8789          Tile[x][y] == EL_AMOEBA_DEAD) &&
8790         AmoebaNr[x][y] != new_group_nr)
8791     {
8792       int old_group_nr = AmoebaNr[x][y];
8793
8794       if (old_group_nr == 0)
8795         return;
8796
8797       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8798       AmoebaCnt[old_group_nr] = 0;
8799       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8800       AmoebaCnt2[old_group_nr] = 0;
8801
8802       SCAN_PLAYFIELD(xx, yy)
8803       {
8804         if (AmoebaNr[xx][yy] == old_group_nr)
8805           AmoebaNr[xx][yy] = new_group_nr;
8806       }
8807     }
8808   }
8809 }
8810
8811 void AmoebaToDiamond(int ax, int ay)
8812 {
8813   int i, x, y;
8814
8815   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
8816   {
8817     int group_nr = AmoebaNr[ax][ay];
8818
8819 #ifdef DEBUG
8820     if (group_nr == 0)
8821     {
8822       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
8823       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
8824
8825       return;
8826     }
8827 #endif
8828
8829     SCAN_PLAYFIELD(x, y)
8830     {
8831       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8832       {
8833         AmoebaNr[x][y] = 0;
8834         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
8835       }
8836     }
8837
8838     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8839                             SND_AMOEBA_TURNING_TO_GEM :
8840                             SND_AMOEBA_TURNING_TO_ROCK));
8841     Bang(ax, ay);
8842   }
8843   else
8844   {
8845     static int xy[4][2] =
8846     {
8847       { 0, -1 },
8848       { -1, 0 },
8849       { +1, 0 },
8850       { 0, +1 }
8851     };
8852
8853     for (i = 0; i < NUM_DIRECTIONS; i++)
8854     {
8855       x = ax + xy[i][0];
8856       y = ay + xy[i][1];
8857
8858       if (!IN_LEV_FIELD(x, y))
8859         continue;
8860
8861       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
8862       {
8863         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8864                               SND_AMOEBA_TURNING_TO_GEM :
8865                               SND_AMOEBA_TURNING_TO_ROCK));
8866         Bang(x, y);
8867       }
8868     }
8869   }
8870 }
8871
8872 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
8873 {
8874   int x, y;
8875   int group_nr = AmoebaNr[ax][ay];
8876   boolean done = FALSE;
8877
8878 #ifdef DEBUG
8879   if (group_nr == 0)
8880   {
8881     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
8882     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
8883
8884     return;
8885   }
8886 #endif
8887
8888   SCAN_PLAYFIELD(x, y)
8889   {
8890     if (AmoebaNr[x][y] == group_nr &&
8891         (Tile[x][y] == EL_AMOEBA_DEAD ||
8892          Tile[x][y] == EL_BD_AMOEBA ||
8893          Tile[x][y] == EL_AMOEBA_GROWING))
8894     {
8895       AmoebaNr[x][y] = 0;
8896       Tile[x][y] = new_element;
8897       InitField(x, y, FALSE);
8898       TEST_DrawLevelField(x, y);
8899       done = TRUE;
8900     }
8901   }
8902
8903   if (done)
8904     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8905                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8906                             SND_BD_AMOEBA_TURNING_TO_GEM));
8907 }
8908
8909 static void AmoebaGrowing(int x, int y)
8910 {
8911   static unsigned int sound_delay = 0;
8912   static unsigned int sound_delay_value = 0;
8913
8914   if (!MovDelay[x][y])          // start new growing cycle
8915   {
8916     MovDelay[x][y] = 7;
8917
8918     if (DelayReached(&sound_delay, sound_delay_value))
8919     {
8920       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8921       sound_delay_value = 30;
8922     }
8923   }
8924
8925   if (MovDelay[x][y])           // wait some time before growing bigger
8926   {
8927     MovDelay[x][y]--;
8928     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8929     {
8930       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8931                                            6 - MovDelay[x][y]);
8932
8933       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8934     }
8935
8936     if (!MovDelay[x][y])
8937     {
8938       Tile[x][y] = Store[x][y];
8939       Store[x][y] = 0;
8940       TEST_DrawLevelField(x, y);
8941     }
8942   }
8943 }
8944
8945 static void AmoebaShrinking(int x, int y)
8946 {
8947   static unsigned int sound_delay = 0;
8948   static unsigned int sound_delay_value = 0;
8949
8950   if (!MovDelay[x][y])          // start new shrinking cycle
8951   {
8952     MovDelay[x][y] = 7;
8953
8954     if (DelayReached(&sound_delay, sound_delay_value))
8955       sound_delay_value = 30;
8956   }
8957
8958   if (MovDelay[x][y])           // wait some time before shrinking
8959   {
8960     MovDelay[x][y]--;
8961     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8962     {
8963       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8964                                            6 - MovDelay[x][y]);
8965
8966       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8967     }
8968
8969     if (!MovDelay[x][y])
8970     {
8971       Tile[x][y] = EL_EMPTY;
8972       TEST_DrawLevelField(x, y);
8973
8974       // don't let mole enter this field in this cycle;
8975       // (give priority to objects falling to this field from above)
8976       Stop[x][y] = TRUE;
8977     }
8978   }
8979 }
8980
8981 static void AmoebaReproduce(int ax, int ay)
8982 {
8983   int i;
8984   int element = Tile[ax][ay];
8985   int graphic = el2img(element);
8986   int newax = ax, neway = ay;
8987   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8988   static int xy[4][2] =
8989   {
8990     { 0, -1 },
8991     { -1, 0 },
8992     { +1, 0 },
8993     { 0, +1 }
8994   };
8995
8996   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8997   {
8998     Tile[ax][ay] = EL_AMOEBA_DEAD;
8999     TEST_DrawLevelField(ax, ay);
9000     return;
9001   }
9002
9003   if (IS_ANIMATED(graphic))
9004     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9005
9006   if (!MovDelay[ax][ay])        // start making new amoeba field
9007     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9008
9009   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9010   {
9011     MovDelay[ax][ay]--;
9012     if (MovDelay[ax][ay])
9013       return;
9014   }
9015
9016   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9017   {
9018     int start = RND(4);
9019     int x = ax + xy[start][0];
9020     int y = ay + xy[start][1];
9021
9022     if (!IN_LEV_FIELD(x, y))
9023       return;
9024
9025     if (IS_FREE(x, y) ||
9026         CAN_GROW_INTO(Tile[x][y]) ||
9027         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9028         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9029     {
9030       newax = x;
9031       neway = y;
9032     }
9033
9034     if (newax == ax && neway == ay)
9035       return;
9036   }
9037   else                          // normal or "filled" (BD style) amoeba
9038   {
9039     int start = RND(4);
9040     boolean waiting_for_player = FALSE;
9041
9042     for (i = 0; i < NUM_DIRECTIONS; i++)
9043     {
9044       int j = (start + i) % 4;
9045       int x = ax + xy[j][0];
9046       int y = ay + xy[j][1];
9047
9048       if (!IN_LEV_FIELD(x, y))
9049         continue;
9050
9051       if (IS_FREE(x, y) ||
9052           CAN_GROW_INTO(Tile[x][y]) ||
9053           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9054           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9055       {
9056         newax = x;
9057         neway = y;
9058         break;
9059       }
9060       else if (IS_PLAYER(x, y))
9061         waiting_for_player = TRUE;
9062     }
9063
9064     if (newax == ax && neway == ay)             // amoeba cannot grow
9065     {
9066       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9067       {
9068         Tile[ax][ay] = EL_AMOEBA_DEAD;
9069         TEST_DrawLevelField(ax, ay);
9070         AmoebaCnt[AmoebaNr[ax][ay]]--;
9071
9072         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9073         {
9074           if (element == EL_AMOEBA_FULL)
9075             AmoebaToDiamond(ax, ay);
9076           else if (element == EL_BD_AMOEBA)
9077             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9078         }
9079       }
9080       return;
9081     }
9082     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9083     {
9084       // amoeba gets larger by growing in some direction
9085
9086       int new_group_nr = AmoebaNr[ax][ay];
9087
9088 #ifdef DEBUG
9089   if (new_group_nr == 0)
9090   {
9091     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9092           newax, neway);
9093     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9094
9095     return;
9096   }
9097 #endif
9098
9099       AmoebaNr[newax][neway] = new_group_nr;
9100       AmoebaCnt[new_group_nr]++;
9101       AmoebaCnt2[new_group_nr]++;
9102
9103       // if amoeba touches other amoeba(s) after growing, unify them
9104       AmoebaMerge(newax, neway);
9105
9106       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9107       {
9108         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9109         return;
9110       }
9111     }
9112   }
9113
9114   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9115       (neway == lev_fieldy - 1 && newax != ax))
9116   {
9117     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9118     Store[newax][neway] = element;
9119   }
9120   else if (neway == ay || element == EL_EMC_DRIPPER)
9121   {
9122     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9123
9124     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9125   }
9126   else
9127   {
9128     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9129     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9130     Store[ax][ay] = EL_AMOEBA_DROP;
9131     ContinueMoving(ax, ay);
9132     return;
9133   }
9134
9135   TEST_DrawLevelField(newax, neway);
9136 }
9137
9138 static void Life(int ax, int ay)
9139 {
9140   int x1, y1, x2, y2;
9141   int life_time = 40;
9142   int element = Tile[ax][ay];
9143   int graphic = el2img(element);
9144   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9145                          level.biomaze);
9146   boolean changed = FALSE;
9147
9148   if (IS_ANIMATED(graphic))
9149     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9150
9151   if (Stop[ax][ay])
9152     return;
9153
9154   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9155     MovDelay[ax][ay] = life_time;
9156
9157   if (MovDelay[ax][ay])         // wait some time before next cycle
9158   {
9159     MovDelay[ax][ay]--;
9160     if (MovDelay[ax][ay])
9161       return;
9162   }
9163
9164   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9165   {
9166     int xx = ax+x1, yy = ay+y1;
9167     int old_element = Tile[xx][yy];
9168     int num_neighbours = 0;
9169
9170     if (!IN_LEV_FIELD(xx, yy))
9171       continue;
9172
9173     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9174     {
9175       int x = xx+x2, y = yy+y2;
9176
9177       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9178         continue;
9179
9180       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9181       boolean is_neighbour = FALSE;
9182
9183       if (level.use_life_bugs)
9184         is_neighbour =
9185           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9186            (IS_FREE(x, y)                             &&  Stop[x][y]));
9187       else
9188         is_neighbour =
9189           (Last[x][y] == element || is_player_cell);
9190
9191       if (is_neighbour)
9192         num_neighbours++;
9193     }
9194
9195     boolean is_free = FALSE;
9196
9197     if (level.use_life_bugs)
9198       is_free = (IS_FREE(xx, yy));
9199     else
9200       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9201
9202     if (xx == ax && yy == ay)           // field in the middle
9203     {
9204       if (num_neighbours < life_parameter[0] ||
9205           num_neighbours > life_parameter[1])
9206       {
9207         Tile[xx][yy] = EL_EMPTY;
9208         if (Tile[xx][yy] != old_element)
9209           TEST_DrawLevelField(xx, yy);
9210         Stop[xx][yy] = TRUE;
9211         changed = TRUE;
9212       }
9213     }
9214     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9215     {                                   // free border field
9216       if (num_neighbours >= life_parameter[2] &&
9217           num_neighbours <= life_parameter[3])
9218       {
9219         Tile[xx][yy] = element;
9220         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9221         if (Tile[xx][yy] != old_element)
9222           TEST_DrawLevelField(xx, yy);
9223         Stop[xx][yy] = TRUE;
9224         changed = TRUE;
9225       }
9226     }
9227   }
9228
9229   if (changed)
9230     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9231                    SND_GAME_OF_LIFE_GROWING);
9232 }
9233
9234 static void InitRobotWheel(int x, int y)
9235 {
9236   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9237 }
9238
9239 static void RunRobotWheel(int x, int y)
9240 {
9241   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9242 }
9243
9244 static void StopRobotWheel(int x, int y)
9245 {
9246   if (game.robot_wheel_x == x &&
9247       game.robot_wheel_y == y)
9248   {
9249     game.robot_wheel_x = -1;
9250     game.robot_wheel_y = -1;
9251     game.robot_wheel_active = FALSE;
9252   }
9253 }
9254
9255 static void InitTimegateWheel(int x, int y)
9256 {
9257   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9258 }
9259
9260 static void RunTimegateWheel(int x, int y)
9261 {
9262   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9263 }
9264
9265 static void InitMagicBallDelay(int x, int y)
9266 {
9267   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9268 }
9269
9270 static void ActivateMagicBall(int bx, int by)
9271 {
9272   int x, y;
9273
9274   if (level.ball_random)
9275   {
9276     int pos_border = RND(8);    // select one of the eight border elements
9277     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9278     int xx = pos_content % 3;
9279     int yy = pos_content / 3;
9280
9281     x = bx - 1 + xx;
9282     y = by - 1 + yy;
9283
9284     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9285       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9286   }
9287   else
9288   {
9289     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9290     {
9291       int xx = x - bx + 1;
9292       int yy = y - by + 1;
9293
9294       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9295         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9296     }
9297   }
9298
9299   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9300 }
9301
9302 static void CheckExit(int x, int y)
9303 {
9304   if (game.gems_still_needed > 0 ||
9305       game.sokoban_fields_still_needed > 0 ||
9306       game.sokoban_objects_still_needed > 0 ||
9307       game.lights_still_needed > 0)
9308   {
9309     int element = Tile[x][y];
9310     int graphic = el2img(element);
9311
9312     if (IS_ANIMATED(graphic))
9313       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9314
9315     return;
9316   }
9317
9318   // do not re-open exit door closed after last player
9319   if (game.all_players_gone)
9320     return;
9321
9322   Tile[x][y] = EL_EXIT_OPENING;
9323
9324   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9325 }
9326
9327 static void CheckExitEM(int x, int y)
9328 {
9329   if (game.gems_still_needed > 0 ||
9330       game.sokoban_fields_still_needed > 0 ||
9331       game.sokoban_objects_still_needed > 0 ||
9332       game.lights_still_needed > 0)
9333   {
9334     int element = Tile[x][y];
9335     int graphic = el2img(element);
9336
9337     if (IS_ANIMATED(graphic))
9338       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9339
9340     return;
9341   }
9342
9343   // do not re-open exit door closed after last player
9344   if (game.all_players_gone)
9345     return;
9346
9347   Tile[x][y] = EL_EM_EXIT_OPENING;
9348
9349   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9350 }
9351
9352 static void CheckExitSteel(int x, int y)
9353 {
9354   if (game.gems_still_needed > 0 ||
9355       game.sokoban_fields_still_needed > 0 ||
9356       game.sokoban_objects_still_needed > 0 ||
9357       game.lights_still_needed > 0)
9358   {
9359     int element = Tile[x][y];
9360     int graphic = el2img(element);
9361
9362     if (IS_ANIMATED(graphic))
9363       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9364
9365     return;
9366   }
9367
9368   // do not re-open exit door closed after last player
9369   if (game.all_players_gone)
9370     return;
9371
9372   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9373
9374   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9375 }
9376
9377 static void CheckExitSteelEM(int x, int y)
9378 {
9379   if (game.gems_still_needed > 0 ||
9380       game.sokoban_fields_still_needed > 0 ||
9381       game.sokoban_objects_still_needed > 0 ||
9382       game.lights_still_needed > 0)
9383   {
9384     int element = Tile[x][y];
9385     int graphic = el2img(element);
9386
9387     if (IS_ANIMATED(graphic))
9388       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9389
9390     return;
9391   }
9392
9393   // do not re-open exit door closed after last player
9394   if (game.all_players_gone)
9395     return;
9396
9397   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9398
9399   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9400 }
9401
9402 static void CheckExitSP(int x, int y)
9403 {
9404   if (game.gems_still_needed > 0)
9405   {
9406     int element = Tile[x][y];
9407     int graphic = el2img(element);
9408
9409     if (IS_ANIMATED(graphic))
9410       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9411
9412     return;
9413   }
9414
9415   // do not re-open exit door closed after last player
9416   if (game.all_players_gone)
9417     return;
9418
9419   Tile[x][y] = EL_SP_EXIT_OPENING;
9420
9421   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9422 }
9423
9424 static void CloseAllOpenTimegates(void)
9425 {
9426   int x, y;
9427
9428   SCAN_PLAYFIELD(x, y)
9429   {
9430     int element = Tile[x][y];
9431
9432     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9433     {
9434       Tile[x][y] = EL_TIMEGATE_CLOSING;
9435
9436       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9437     }
9438   }
9439 }
9440
9441 static void DrawTwinkleOnField(int x, int y)
9442 {
9443   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9444     return;
9445
9446   if (Tile[x][y] == EL_BD_DIAMOND)
9447     return;
9448
9449   if (MovDelay[x][y] == 0)      // next animation frame
9450     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9451
9452   if (MovDelay[x][y] != 0)      // wait some time before next frame
9453   {
9454     MovDelay[x][y]--;
9455
9456     DrawLevelElementAnimation(x, y, Tile[x][y]);
9457
9458     if (MovDelay[x][y] != 0)
9459     {
9460       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9461                                            10 - MovDelay[x][y]);
9462
9463       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9464     }
9465   }
9466 }
9467
9468 static void MauerWaechst(int x, int y)
9469 {
9470   int delay = 6;
9471
9472   if (!MovDelay[x][y])          // next animation frame
9473     MovDelay[x][y] = 3 * delay;
9474
9475   if (MovDelay[x][y])           // wait some time before next frame
9476   {
9477     MovDelay[x][y]--;
9478
9479     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9480     {
9481       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9482       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9483
9484       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9485     }
9486
9487     if (!MovDelay[x][y])
9488     {
9489       if (MovDir[x][y] == MV_LEFT)
9490       {
9491         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9492           TEST_DrawLevelField(x - 1, y);
9493       }
9494       else if (MovDir[x][y] == MV_RIGHT)
9495       {
9496         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9497           TEST_DrawLevelField(x + 1, y);
9498       }
9499       else if (MovDir[x][y] == MV_UP)
9500       {
9501         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9502           TEST_DrawLevelField(x, y - 1);
9503       }
9504       else
9505       {
9506         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9507           TEST_DrawLevelField(x, y + 1);
9508       }
9509
9510       Tile[x][y] = Store[x][y];
9511       Store[x][y] = 0;
9512       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9513       TEST_DrawLevelField(x, y);
9514     }
9515   }
9516 }
9517
9518 static void MauerAbleger(int ax, int ay)
9519 {
9520   int element = Tile[ax][ay];
9521   int graphic = el2img(element);
9522   boolean oben_frei = FALSE, unten_frei = FALSE;
9523   boolean links_frei = FALSE, rechts_frei = FALSE;
9524   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9525   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9526   boolean new_wall = FALSE;
9527
9528   if (IS_ANIMATED(graphic))
9529     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9530
9531   if (!MovDelay[ax][ay])        // start building new wall
9532     MovDelay[ax][ay] = 6;
9533
9534   if (MovDelay[ax][ay])         // wait some time before building new wall
9535   {
9536     MovDelay[ax][ay]--;
9537     if (MovDelay[ax][ay])
9538       return;
9539   }
9540
9541   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9542     oben_frei = TRUE;
9543   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9544     unten_frei = TRUE;
9545   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9546     links_frei = TRUE;
9547   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9548     rechts_frei = TRUE;
9549
9550   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9551       element == EL_EXPANDABLE_WALL_ANY)
9552   {
9553     if (oben_frei)
9554     {
9555       Tile[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9556       Store[ax][ay-1] = element;
9557       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9558       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9559         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9560                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9561       new_wall = TRUE;
9562     }
9563     if (unten_frei)
9564     {
9565       Tile[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9566       Store[ax][ay+1] = element;
9567       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9568       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9569         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9570                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9571       new_wall = TRUE;
9572     }
9573   }
9574
9575   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9576       element == EL_EXPANDABLE_WALL_ANY ||
9577       element == EL_EXPANDABLE_WALL ||
9578       element == EL_BD_EXPANDABLE_WALL)
9579   {
9580     if (links_frei)
9581     {
9582       Tile[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9583       Store[ax-1][ay] = element;
9584       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9585       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9586         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9587                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9588       new_wall = TRUE;
9589     }
9590
9591     if (rechts_frei)
9592     {
9593       Tile[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9594       Store[ax+1][ay] = element;
9595       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9596       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9597         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9598                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9599       new_wall = TRUE;
9600     }
9601   }
9602
9603   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9604     TEST_DrawLevelField(ax, ay);
9605
9606   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9607     oben_massiv = TRUE;
9608   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9609     unten_massiv = TRUE;
9610   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9611     links_massiv = TRUE;
9612   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9613     rechts_massiv = TRUE;
9614
9615   if (((oben_massiv && unten_massiv) ||
9616        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9617        element == EL_EXPANDABLE_WALL) &&
9618       ((links_massiv && rechts_massiv) ||
9619        element == EL_EXPANDABLE_WALL_VERTICAL))
9620     Tile[ax][ay] = EL_WALL;
9621
9622   if (new_wall)
9623     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9624 }
9625
9626 static void MauerAblegerStahl(int ax, int ay)
9627 {
9628   int element = Tile[ax][ay];
9629   int graphic = el2img(element);
9630   boolean oben_frei = FALSE, unten_frei = FALSE;
9631   boolean links_frei = FALSE, rechts_frei = FALSE;
9632   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9633   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9634   boolean new_wall = FALSE;
9635
9636   if (IS_ANIMATED(graphic))
9637     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9638
9639   if (!MovDelay[ax][ay])        // start building new wall
9640     MovDelay[ax][ay] = 6;
9641
9642   if (MovDelay[ax][ay])         // wait some time before building new wall
9643   {
9644     MovDelay[ax][ay]--;
9645     if (MovDelay[ax][ay])
9646       return;
9647   }
9648
9649   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9650     oben_frei = TRUE;
9651   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9652     unten_frei = TRUE;
9653   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9654     links_frei = TRUE;
9655   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9656     rechts_frei = TRUE;
9657
9658   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9659       element == EL_EXPANDABLE_STEELWALL_ANY)
9660   {
9661     if (oben_frei)
9662     {
9663       Tile[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9664       Store[ax][ay-1] = element;
9665       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9666       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9667         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9668                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9669       new_wall = TRUE;
9670     }
9671     if (unten_frei)
9672     {
9673       Tile[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9674       Store[ax][ay+1] = element;
9675       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9676       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9677         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9678                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9679       new_wall = TRUE;
9680     }
9681   }
9682
9683   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9684       element == EL_EXPANDABLE_STEELWALL_ANY)
9685   {
9686     if (links_frei)
9687     {
9688       Tile[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9689       Store[ax-1][ay] = element;
9690       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9691       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9692         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9693                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9694       new_wall = TRUE;
9695     }
9696
9697     if (rechts_frei)
9698     {
9699       Tile[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9700       Store[ax+1][ay] = element;
9701       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9702       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9703         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9704                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9705       new_wall = TRUE;
9706     }
9707   }
9708
9709   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9710     oben_massiv = TRUE;
9711   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9712     unten_massiv = TRUE;
9713   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9714     links_massiv = TRUE;
9715   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9716     rechts_massiv = TRUE;
9717
9718   if (((oben_massiv && unten_massiv) ||
9719        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9720       ((links_massiv && rechts_massiv) ||
9721        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9722     Tile[ax][ay] = EL_STEELWALL;
9723
9724   if (new_wall)
9725     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9726 }
9727
9728 static void CheckForDragon(int x, int y)
9729 {
9730   int i, j;
9731   boolean dragon_found = FALSE;
9732   static int xy[4][2] =
9733   {
9734     { 0, -1 },
9735     { -1, 0 },
9736     { +1, 0 },
9737     { 0, +1 }
9738   };
9739
9740   for (i = 0; i < NUM_DIRECTIONS; i++)
9741   {
9742     for (j = 0; j < 4; j++)
9743     {
9744       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9745
9746       if (IN_LEV_FIELD(xx, yy) &&
9747           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9748       {
9749         if (Tile[xx][yy] == EL_DRAGON)
9750           dragon_found = TRUE;
9751       }
9752       else
9753         break;
9754     }
9755   }
9756
9757   if (!dragon_found)
9758   {
9759     for (i = 0; i < NUM_DIRECTIONS; i++)
9760     {
9761       for (j = 0; j < 3; j++)
9762       {
9763         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9764   
9765         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9766         {
9767           Tile[xx][yy] = EL_EMPTY;
9768           TEST_DrawLevelField(xx, yy);
9769         }
9770         else
9771           break;
9772       }
9773     }
9774   }
9775 }
9776
9777 static void InitBuggyBase(int x, int y)
9778 {
9779   int element = Tile[x][y];
9780   int activating_delay = FRAMES_PER_SECOND / 4;
9781
9782   ChangeDelay[x][y] =
9783     (element == EL_SP_BUGGY_BASE ?
9784      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9785      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9786      activating_delay :
9787      element == EL_SP_BUGGY_BASE_ACTIVE ?
9788      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9789 }
9790
9791 static void WarnBuggyBase(int x, int y)
9792 {
9793   int i;
9794   static int xy[4][2] =
9795   {
9796     { 0, -1 },
9797     { -1, 0 },
9798     { +1, 0 },
9799     { 0, +1 }
9800   };
9801
9802   for (i = 0; i < NUM_DIRECTIONS; i++)
9803   {
9804     int xx = x + xy[i][0];
9805     int yy = y + xy[i][1];
9806
9807     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9808     {
9809       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9810
9811       break;
9812     }
9813   }
9814 }
9815
9816 static void InitTrap(int x, int y)
9817 {
9818   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9819 }
9820
9821 static void ActivateTrap(int x, int y)
9822 {
9823   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9824 }
9825
9826 static void ChangeActiveTrap(int x, int y)
9827 {
9828   int graphic = IMG_TRAP_ACTIVE;
9829
9830   // if new animation frame was drawn, correct crumbled sand border
9831   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9832     TEST_DrawLevelFieldCrumbled(x, y);
9833 }
9834
9835 static int getSpecialActionElement(int element, int number, int base_element)
9836 {
9837   return (element != EL_EMPTY ? element :
9838           number != -1 ? base_element + number - 1 :
9839           EL_EMPTY);
9840 }
9841
9842 static int getModifiedActionNumber(int value_old, int operator, int operand,
9843                                    int value_min, int value_max)
9844 {
9845   int value_new = (operator == CA_MODE_SET      ? operand :
9846                    operator == CA_MODE_ADD      ? value_old + operand :
9847                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9848                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9849                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9850                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9851                    value_old);
9852
9853   return (value_new < value_min ? value_min :
9854           value_new > value_max ? value_max :
9855           value_new);
9856 }
9857
9858 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9859 {
9860   struct ElementInfo *ei = &element_info[element];
9861   struct ElementChangeInfo *change = &ei->change_page[page];
9862   int target_element = change->target_element;
9863   int action_type = change->action_type;
9864   int action_mode = change->action_mode;
9865   int action_arg = change->action_arg;
9866   int action_element = change->action_element;
9867   int i;
9868
9869   if (!change->has_action)
9870     return;
9871
9872   // ---------- determine action paramater values -----------------------------
9873
9874   int level_time_value =
9875     (level.time > 0 ? TimeLeft :
9876      TimePlayed);
9877
9878   int action_arg_element_raw =
9879     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9880      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9881      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9882      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9883      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9884      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9885      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9886      EL_EMPTY);
9887   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9888
9889   int action_arg_direction =
9890     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9891      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9892      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9893      change->actual_trigger_side :
9894      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9895      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9896      MV_NONE);
9897
9898   int action_arg_number_min =
9899     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9900      CA_ARG_MIN);
9901
9902   int action_arg_number_max =
9903     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9904      action_type == CA_SET_LEVEL_GEMS ? 999 :
9905      action_type == CA_SET_LEVEL_TIME ? 9999 :
9906      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9907      action_type == CA_SET_CE_VALUE ? 9999 :
9908      action_type == CA_SET_CE_SCORE ? 9999 :
9909      CA_ARG_MAX);
9910
9911   int action_arg_number_reset =
9912     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9913      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9914      action_type == CA_SET_LEVEL_TIME ? level.time :
9915      action_type == CA_SET_LEVEL_SCORE ? 0 :
9916      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9917      action_type == CA_SET_CE_SCORE ? 0 :
9918      0);
9919
9920   int action_arg_number =
9921     (action_arg <= CA_ARG_MAX ? action_arg :
9922      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9923      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9924      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9925      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9926      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9927      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9928      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9929      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9930      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9931      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9932      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
9933      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
9934      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9935      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9936      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9937      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9938      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9939      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9940      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9941      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9942      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9943      -1);
9944
9945   int action_arg_number_old =
9946     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
9947      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9948      action_type == CA_SET_LEVEL_SCORE ? game.score :
9949      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9950      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9951      0);
9952
9953   int action_arg_number_new =
9954     getModifiedActionNumber(action_arg_number_old,
9955                             action_mode, action_arg_number,
9956                             action_arg_number_min, action_arg_number_max);
9957
9958   int trigger_player_bits =
9959     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9960      change->actual_trigger_player_bits : change->trigger_player);
9961
9962   int action_arg_player_bits =
9963     (action_arg >= CA_ARG_PLAYER_1 &&
9964      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9965      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9966      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9967      PLAYER_BITS_ANY);
9968
9969   // ---------- execute action  -----------------------------------------------
9970
9971   switch (action_type)
9972   {
9973     case CA_NO_ACTION:
9974     {
9975       return;
9976     }
9977
9978     // ---------- level actions  ----------------------------------------------
9979
9980     case CA_RESTART_LEVEL:
9981     {
9982       game.restart_level = TRUE;
9983
9984       break;
9985     }
9986
9987     case CA_SHOW_ENVELOPE:
9988     {
9989       int element = getSpecialActionElement(action_arg_element,
9990                                             action_arg_number, EL_ENVELOPE_1);
9991
9992       if (IS_ENVELOPE(element))
9993         local_player->show_envelope = element;
9994
9995       break;
9996     }
9997
9998     case CA_SET_LEVEL_TIME:
9999     {
10000       if (level.time > 0)       // only modify limited time value
10001       {
10002         TimeLeft = action_arg_number_new;
10003
10004         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10005
10006         DisplayGameControlValues();
10007
10008         if (!TimeLeft && setup.time_limit)
10009           for (i = 0; i < MAX_PLAYERS; i++)
10010             KillPlayer(&stored_player[i]);
10011       }
10012
10013       break;
10014     }
10015
10016     case CA_SET_LEVEL_SCORE:
10017     {
10018       game.score = action_arg_number_new;
10019
10020       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10021
10022       DisplayGameControlValues();
10023
10024       break;
10025     }
10026
10027     case CA_SET_LEVEL_GEMS:
10028     {
10029       game.gems_still_needed = action_arg_number_new;
10030
10031       game.snapshot.collected_item = TRUE;
10032
10033       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10034
10035       DisplayGameControlValues();
10036
10037       break;
10038     }
10039
10040     case CA_SET_LEVEL_WIND:
10041     {
10042       game.wind_direction = action_arg_direction;
10043
10044       break;
10045     }
10046
10047     case CA_SET_LEVEL_RANDOM_SEED:
10048     {
10049       // ensure that setting a new random seed while playing is predictable
10050       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10051
10052       break;
10053     }
10054
10055     // ---------- player actions  ---------------------------------------------
10056
10057     case CA_MOVE_PLAYER:
10058     case CA_MOVE_PLAYER_NEW:
10059     {
10060       // automatically move to the next field in specified direction
10061       for (i = 0; i < MAX_PLAYERS; i++)
10062         if (trigger_player_bits & (1 << i))
10063           if (action_type == CA_MOVE_PLAYER ||
10064               stored_player[i].MovPos == 0)
10065             stored_player[i].programmed_action = action_arg_direction;
10066
10067       break;
10068     }
10069
10070     case CA_EXIT_PLAYER:
10071     {
10072       for (i = 0; i < MAX_PLAYERS; i++)
10073         if (action_arg_player_bits & (1 << i))
10074           ExitPlayer(&stored_player[i]);
10075
10076       if (game.players_still_needed == 0)
10077         LevelSolved();
10078
10079       break;
10080     }
10081
10082     case CA_KILL_PLAYER:
10083     {
10084       for (i = 0; i < MAX_PLAYERS; i++)
10085         if (action_arg_player_bits & (1 << i))
10086           KillPlayer(&stored_player[i]);
10087
10088       break;
10089     }
10090
10091     case CA_SET_PLAYER_KEYS:
10092     {
10093       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10094       int element = getSpecialActionElement(action_arg_element,
10095                                             action_arg_number, EL_KEY_1);
10096
10097       if (IS_KEY(element))
10098       {
10099         for (i = 0; i < MAX_PLAYERS; i++)
10100         {
10101           if (trigger_player_bits & (1 << i))
10102           {
10103             stored_player[i].key[KEY_NR(element)] = key_state;
10104
10105             DrawGameDoorValues();
10106           }
10107         }
10108       }
10109
10110       break;
10111     }
10112
10113     case CA_SET_PLAYER_SPEED:
10114     {
10115       for (i = 0; i < MAX_PLAYERS; i++)
10116       {
10117         if (trigger_player_bits & (1 << i))
10118         {
10119           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10120
10121           if (action_arg == CA_ARG_SPEED_FASTER &&
10122               stored_player[i].cannot_move)
10123           {
10124             action_arg_number = STEPSIZE_VERY_SLOW;
10125           }
10126           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10127                    action_arg == CA_ARG_SPEED_FASTER)
10128           {
10129             action_arg_number = 2;
10130             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10131                            CA_MODE_MULTIPLY);
10132           }
10133           else if (action_arg == CA_ARG_NUMBER_RESET)
10134           {
10135             action_arg_number = level.initial_player_stepsize[i];
10136           }
10137
10138           move_stepsize =
10139             getModifiedActionNumber(move_stepsize,
10140                                     action_mode,
10141                                     action_arg_number,
10142                                     action_arg_number_min,
10143                                     action_arg_number_max);
10144
10145           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10146         }
10147       }
10148
10149       break;
10150     }
10151
10152     case CA_SET_PLAYER_SHIELD:
10153     {
10154       for (i = 0; i < MAX_PLAYERS; i++)
10155       {
10156         if (trigger_player_bits & (1 << i))
10157         {
10158           if (action_arg == CA_ARG_SHIELD_OFF)
10159           {
10160             stored_player[i].shield_normal_time_left = 0;
10161             stored_player[i].shield_deadly_time_left = 0;
10162           }
10163           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10164           {
10165             stored_player[i].shield_normal_time_left = 999999;
10166           }
10167           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10168           {
10169             stored_player[i].shield_normal_time_left = 999999;
10170             stored_player[i].shield_deadly_time_left = 999999;
10171           }
10172         }
10173       }
10174
10175       break;
10176     }
10177
10178     case CA_SET_PLAYER_GRAVITY:
10179     {
10180       for (i = 0; i < MAX_PLAYERS; i++)
10181       {
10182         if (trigger_player_bits & (1 << i))
10183         {
10184           stored_player[i].gravity =
10185             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10186              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10187              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10188              stored_player[i].gravity);
10189         }
10190       }
10191
10192       break;
10193     }
10194
10195     case CA_SET_PLAYER_ARTWORK:
10196     {
10197       for (i = 0; i < MAX_PLAYERS; i++)
10198       {
10199         if (trigger_player_bits & (1 << i))
10200         {
10201           int artwork_element = action_arg_element;
10202
10203           if (action_arg == CA_ARG_ELEMENT_RESET)
10204             artwork_element =
10205               (level.use_artwork_element[i] ? level.artwork_element[i] :
10206                stored_player[i].element_nr);
10207
10208           if (stored_player[i].artwork_element != artwork_element)
10209             stored_player[i].Frame = 0;
10210
10211           stored_player[i].artwork_element = artwork_element;
10212
10213           SetPlayerWaiting(&stored_player[i], FALSE);
10214
10215           // set number of special actions for bored and sleeping animation
10216           stored_player[i].num_special_action_bored =
10217             get_num_special_action(artwork_element,
10218                                    ACTION_BORING_1, ACTION_BORING_LAST);
10219           stored_player[i].num_special_action_sleeping =
10220             get_num_special_action(artwork_element,
10221                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10222         }
10223       }
10224
10225       break;
10226     }
10227
10228     case CA_SET_PLAYER_INVENTORY:
10229     {
10230       for (i = 0; i < MAX_PLAYERS; i++)
10231       {
10232         struct PlayerInfo *player = &stored_player[i];
10233         int j, k;
10234
10235         if (trigger_player_bits & (1 << i))
10236         {
10237           int inventory_element = action_arg_element;
10238
10239           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10240               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10241               action_arg == CA_ARG_ELEMENT_ACTION)
10242           {
10243             int element = inventory_element;
10244             int collect_count = element_info[element].collect_count_initial;
10245
10246             if (!IS_CUSTOM_ELEMENT(element))
10247               collect_count = 1;
10248
10249             if (collect_count == 0)
10250               player->inventory_infinite_element = element;
10251             else
10252               for (k = 0; k < collect_count; k++)
10253                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10254                   player->inventory_element[player->inventory_size++] =
10255                     element;
10256           }
10257           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10258                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10259                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10260           {
10261             if (player->inventory_infinite_element != EL_UNDEFINED &&
10262                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10263                                      action_arg_element_raw))
10264               player->inventory_infinite_element = EL_UNDEFINED;
10265
10266             for (k = 0, j = 0; j < player->inventory_size; j++)
10267             {
10268               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10269                                         action_arg_element_raw))
10270                 player->inventory_element[k++] = player->inventory_element[j];
10271             }
10272
10273             player->inventory_size = k;
10274           }
10275           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10276           {
10277             if (player->inventory_size > 0)
10278             {
10279               for (j = 0; j < player->inventory_size - 1; j++)
10280                 player->inventory_element[j] = player->inventory_element[j + 1];
10281
10282               player->inventory_size--;
10283             }
10284           }
10285           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10286           {
10287             if (player->inventory_size > 0)
10288               player->inventory_size--;
10289           }
10290           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10291           {
10292             player->inventory_infinite_element = EL_UNDEFINED;
10293             player->inventory_size = 0;
10294           }
10295           else if (action_arg == CA_ARG_INVENTORY_RESET)
10296           {
10297             player->inventory_infinite_element = EL_UNDEFINED;
10298             player->inventory_size = 0;
10299
10300             if (level.use_initial_inventory[i])
10301             {
10302               for (j = 0; j < level.initial_inventory_size[i]; j++)
10303               {
10304                 int element = level.initial_inventory_content[i][j];
10305                 int collect_count = element_info[element].collect_count_initial;
10306
10307                 if (!IS_CUSTOM_ELEMENT(element))
10308                   collect_count = 1;
10309
10310                 if (collect_count == 0)
10311                   player->inventory_infinite_element = element;
10312                 else
10313                   for (k = 0; k < collect_count; k++)
10314                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10315                       player->inventory_element[player->inventory_size++] =
10316                         element;
10317               }
10318             }
10319           }
10320         }
10321       }
10322
10323       break;
10324     }
10325
10326     // ---------- CE actions  -------------------------------------------------
10327
10328     case CA_SET_CE_VALUE:
10329     {
10330       int last_ce_value = CustomValue[x][y];
10331
10332       CustomValue[x][y] = action_arg_number_new;
10333
10334       if (CustomValue[x][y] != last_ce_value)
10335       {
10336         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10337         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10338
10339         if (CustomValue[x][y] == 0)
10340         {
10341           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10342           ChangeCount[x][y] = 0;        // allow at least one more change
10343
10344           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10345           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10346         }
10347       }
10348
10349       break;
10350     }
10351
10352     case CA_SET_CE_SCORE:
10353     {
10354       int last_ce_score = ei->collect_score;
10355
10356       ei->collect_score = action_arg_number_new;
10357
10358       if (ei->collect_score != last_ce_score)
10359       {
10360         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10361         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10362
10363         if (ei->collect_score == 0)
10364         {
10365           int xx, yy;
10366
10367           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10368           ChangeCount[x][y] = 0;        // allow at least one more change
10369
10370           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10371           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10372
10373           /*
10374             This is a very special case that seems to be a mixture between
10375             CheckElementChange() and CheckTriggeredElementChange(): while
10376             the first one only affects single elements that are triggered
10377             directly, the second one affects multiple elements in the playfield
10378             that are triggered indirectly by another element. This is a third
10379             case: Changing the CE score always affects multiple identical CEs,
10380             so every affected CE must be checked, not only the single CE for
10381             which the CE score was changed in the first place (as every instance
10382             of that CE shares the same CE score, and therefore also can change)!
10383           */
10384           SCAN_PLAYFIELD(xx, yy)
10385           {
10386             if (Tile[xx][yy] == element)
10387               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10388                                  CE_SCORE_GETS_ZERO);
10389           }
10390         }
10391       }
10392
10393       break;
10394     }
10395
10396     case CA_SET_CE_ARTWORK:
10397     {
10398       int artwork_element = action_arg_element;
10399       boolean reset_frame = FALSE;
10400       int xx, yy;
10401
10402       if (action_arg == CA_ARG_ELEMENT_RESET)
10403         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10404                            element);
10405
10406       if (ei->gfx_element != artwork_element)
10407         reset_frame = TRUE;
10408
10409       ei->gfx_element = artwork_element;
10410
10411       SCAN_PLAYFIELD(xx, yy)
10412       {
10413         if (Tile[xx][yy] == element)
10414         {
10415           if (reset_frame)
10416           {
10417             ResetGfxAnimation(xx, yy);
10418             ResetRandomAnimationValue(xx, yy);
10419           }
10420
10421           TEST_DrawLevelField(xx, yy);
10422         }
10423       }
10424
10425       break;
10426     }
10427
10428     // ---------- engine actions  ---------------------------------------------
10429
10430     case CA_SET_ENGINE_SCAN_MODE:
10431     {
10432       InitPlayfieldScanMode(action_arg);
10433
10434       break;
10435     }
10436
10437     default:
10438       break;
10439   }
10440 }
10441
10442 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10443 {
10444   int old_element = Tile[x][y];
10445   int new_element = GetElementFromGroupElement(element);
10446   int previous_move_direction = MovDir[x][y];
10447   int last_ce_value = CustomValue[x][y];
10448   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10449   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10450   boolean add_player_onto_element = (new_element_is_player &&
10451                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10452                                      IS_WALKABLE(old_element));
10453
10454   if (!add_player_onto_element)
10455   {
10456     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10457       RemoveMovingField(x, y);
10458     else
10459       RemoveField(x, y);
10460
10461     Tile[x][y] = new_element;
10462
10463     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10464       MovDir[x][y] = previous_move_direction;
10465
10466     if (element_info[new_element].use_last_ce_value)
10467       CustomValue[x][y] = last_ce_value;
10468
10469     InitField_WithBug1(x, y, FALSE);
10470
10471     new_element = Tile[x][y];   // element may have changed
10472
10473     ResetGfxAnimation(x, y);
10474     ResetRandomAnimationValue(x, y);
10475
10476     TEST_DrawLevelField(x, y);
10477
10478     if (GFX_CRUMBLED(new_element))
10479       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10480   }
10481
10482   // check if element under the player changes from accessible to unaccessible
10483   // (needed for special case of dropping element which then changes)
10484   // (must be checked after creating new element for walkable group elements)
10485   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10486       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10487   {
10488     Bang(x, y);
10489
10490     return;
10491   }
10492
10493   // "ChangeCount" not set yet to allow "entered by player" change one time
10494   if (new_element_is_player)
10495     RelocatePlayer(x, y, new_element);
10496
10497   if (is_change)
10498     ChangeCount[x][y]++;        // count number of changes in the same frame
10499
10500   TestIfBadThingTouchesPlayer(x, y);
10501   TestIfPlayerTouchesCustomElement(x, y);
10502   TestIfElementTouchesCustomElement(x, y);
10503 }
10504
10505 static void CreateField(int x, int y, int element)
10506 {
10507   CreateFieldExt(x, y, element, FALSE);
10508 }
10509
10510 static void CreateElementFromChange(int x, int y, int element)
10511 {
10512   element = GET_VALID_RUNTIME_ELEMENT(element);
10513
10514   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10515   {
10516     int old_element = Tile[x][y];
10517
10518     // prevent changed element from moving in same engine frame
10519     // unless both old and new element can either fall or move
10520     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10521         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10522       Stop[x][y] = TRUE;
10523   }
10524
10525   CreateFieldExt(x, y, element, TRUE);
10526 }
10527
10528 static boolean ChangeElement(int x, int y, int element, int page)
10529 {
10530   struct ElementInfo *ei = &element_info[element];
10531   struct ElementChangeInfo *change = &ei->change_page[page];
10532   int ce_value = CustomValue[x][y];
10533   int ce_score = ei->collect_score;
10534   int target_element;
10535   int old_element = Tile[x][y];
10536
10537   // always use default change event to prevent running into a loop
10538   if (ChangeEvent[x][y] == -1)
10539     ChangeEvent[x][y] = CE_DELAY;
10540
10541   if (ChangeEvent[x][y] == CE_DELAY)
10542   {
10543     // reset actual trigger element, trigger player and action element
10544     change->actual_trigger_element = EL_EMPTY;
10545     change->actual_trigger_player = EL_EMPTY;
10546     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10547     change->actual_trigger_side = CH_SIDE_NONE;
10548     change->actual_trigger_ce_value = 0;
10549     change->actual_trigger_ce_score = 0;
10550   }
10551
10552   // do not change elements more than a specified maximum number of changes
10553   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10554     return FALSE;
10555
10556   ChangeCount[x][y]++;          // count number of changes in the same frame
10557
10558   if (change->explode)
10559   {
10560     Bang(x, y);
10561
10562     return TRUE;
10563   }
10564
10565   if (change->use_target_content)
10566   {
10567     boolean complete_replace = TRUE;
10568     boolean can_replace[3][3];
10569     int xx, yy;
10570
10571     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10572     {
10573       boolean is_empty;
10574       boolean is_walkable;
10575       boolean is_diggable;
10576       boolean is_collectible;
10577       boolean is_removable;
10578       boolean is_destructible;
10579       int ex = x + xx - 1;
10580       int ey = y + yy - 1;
10581       int content_element = change->target_content.e[xx][yy];
10582       int e;
10583
10584       can_replace[xx][yy] = TRUE;
10585
10586       if (ex == x && ey == y)   // do not check changing element itself
10587         continue;
10588
10589       if (content_element == EL_EMPTY_SPACE)
10590       {
10591         can_replace[xx][yy] = FALSE;    // do not replace border with space
10592
10593         continue;
10594       }
10595
10596       if (!IN_LEV_FIELD(ex, ey))
10597       {
10598         can_replace[xx][yy] = FALSE;
10599         complete_replace = FALSE;
10600
10601         continue;
10602       }
10603
10604       e = Tile[ex][ey];
10605
10606       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10607         e = MovingOrBlocked2Element(ex, ey);
10608
10609       is_empty = (IS_FREE(ex, ey) ||
10610                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10611
10612       is_walkable     = (is_empty || IS_WALKABLE(e));
10613       is_diggable     = (is_empty || IS_DIGGABLE(e));
10614       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10615       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10616       is_removable    = (is_diggable || is_collectible);
10617
10618       can_replace[xx][yy] =
10619         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10620           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10621           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10622           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10623           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10624           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10625          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10626
10627       if (!can_replace[xx][yy])
10628         complete_replace = FALSE;
10629     }
10630
10631     if (!change->only_if_complete || complete_replace)
10632     {
10633       boolean something_has_changed = FALSE;
10634
10635       if (change->only_if_complete && change->use_random_replace &&
10636           RND(100) < change->random_percentage)
10637         return FALSE;
10638
10639       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10640       {
10641         int ex = x + xx - 1;
10642         int ey = y + yy - 1;
10643         int content_element;
10644
10645         if (can_replace[xx][yy] && (!change->use_random_replace ||
10646                                     RND(100) < change->random_percentage))
10647         {
10648           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10649             RemoveMovingField(ex, ey);
10650
10651           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10652
10653           content_element = change->target_content.e[xx][yy];
10654           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10655                                               ce_value, ce_score);
10656
10657           CreateElementFromChange(ex, ey, target_element);
10658
10659           something_has_changed = TRUE;
10660
10661           // for symmetry reasons, freeze newly created border elements
10662           if (ex != x || ey != y)
10663             Stop[ex][ey] = TRUE;        // no more moving in this frame
10664         }
10665       }
10666
10667       if (something_has_changed)
10668       {
10669         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10670         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10671       }
10672     }
10673   }
10674   else
10675   {
10676     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10677                                         ce_value, ce_score);
10678
10679     if (element == EL_DIAGONAL_GROWING ||
10680         element == EL_DIAGONAL_SHRINKING)
10681     {
10682       target_element = Store[x][y];
10683
10684       Store[x][y] = EL_EMPTY;
10685     }
10686
10687     CreateElementFromChange(x, y, target_element);
10688
10689     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10690     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10691   }
10692
10693   // this uses direct change before indirect change
10694   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10695
10696   return TRUE;
10697 }
10698
10699 static void HandleElementChange(int x, int y, int page)
10700 {
10701   int element = MovingOrBlocked2Element(x, y);
10702   struct ElementInfo *ei = &element_info[element];
10703   struct ElementChangeInfo *change = &ei->change_page[page];
10704   boolean handle_action_before_change = FALSE;
10705
10706 #ifdef DEBUG
10707   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10708       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10709   {
10710     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10711           x, y, element, element_info[element].token_name);
10712     Debug("game:playing:HandleElementChange", "This should never happen!");
10713   }
10714 #endif
10715
10716   // this can happen with classic bombs on walkable, changing elements
10717   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10718   {
10719     return;
10720   }
10721
10722   if (ChangeDelay[x][y] == 0)           // initialize element change
10723   {
10724     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10725
10726     if (change->can_change)
10727     {
10728       // !!! not clear why graphic animation should be reset at all here !!!
10729       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10730       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10731
10732       /*
10733         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10734
10735         When using an animation frame delay of 1 (this only happens with
10736         "sp_zonk.moving.left/right" in the classic graphics), the default
10737         (non-moving) animation shows wrong animation frames (while the
10738         moving animation, like "sp_zonk.moving.left/right", is correct,
10739         so this graphical bug never shows up with the classic graphics).
10740         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10741         be drawn instead of the correct frames 0,1,2,3. This is caused by
10742         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10743         an element change: First when the change delay ("ChangeDelay[][]")
10744         counter has reached zero after decrementing, then a second time in
10745         the next frame (after "GfxFrame[][]" was already incremented) when
10746         "ChangeDelay[][]" is reset to the initial delay value again.
10747
10748         This causes frame 0 to be drawn twice, while the last frame won't
10749         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10750
10751         As some animations may already be cleverly designed around this bug
10752         (at least the "Snake Bite" snake tail animation does this), it cannot
10753         simply be fixed here without breaking such existing animations.
10754         Unfortunately, it cannot easily be detected if a graphics set was
10755         designed "before" or "after" the bug was fixed. As a workaround,
10756         a new graphics set option "game.graphics_engine_version" was added
10757         to be able to specify the game's major release version for which the
10758         graphics set was designed, which can then be used to decide if the
10759         bugfix should be used (version 4 and above) or not (version 3 or
10760         below, or if no version was specified at all, as with old sets).
10761
10762         (The wrong/fixed animation frames can be tested with the test level set
10763         "test_gfxframe" and level "000", which contains a specially prepared
10764         custom element at level position (x/y) == (11/9) which uses the zonk
10765         animation mentioned above. Using "game.graphics_engine_version: 4"
10766         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10767         This can also be seen from the debug output for this test element.)
10768       */
10769
10770       // when a custom element is about to change (for example by change delay),
10771       // do not reset graphic animation when the custom element is moving
10772       if (game.graphics_engine_version < 4 &&
10773           !IS_MOVING(x, y))
10774       {
10775         ResetGfxAnimation(x, y);
10776         ResetRandomAnimationValue(x, y);
10777       }
10778
10779       if (change->pre_change_function)
10780         change->pre_change_function(x, y);
10781     }
10782   }
10783
10784   ChangeDelay[x][y]--;
10785
10786   if (ChangeDelay[x][y] != 0)           // continue element change
10787   {
10788     if (change->can_change)
10789     {
10790       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10791
10792       if (IS_ANIMATED(graphic))
10793         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10794
10795       if (change->change_function)
10796         change->change_function(x, y);
10797     }
10798   }
10799   else                                  // finish element change
10800   {
10801     if (ChangePage[x][y] != -1)         // remember page from delayed change
10802     {
10803       page = ChangePage[x][y];
10804       ChangePage[x][y] = -1;
10805
10806       change = &ei->change_page[page];
10807     }
10808
10809     if (IS_MOVING(x, y))                // never change a running system ;-)
10810     {
10811       ChangeDelay[x][y] = 1;            // try change after next move step
10812       ChangePage[x][y] = page;          // remember page to use for change
10813
10814       return;
10815     }
10816
10817     // special case: set new level random seed before changing element
10818     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10819       handle_action_before_change = TRUE;
10820
10821     if (change->has_action && handle_action_before_change)
10822       ExecuteCustomElementAction(x, y, element, page);
10823
10824     if (change->can_change)
10825     {
10826       if (ChangeElement(x, y, element, page))
10827       {
10828         if (change->post_change_function)
10829           change->post_change_function(x, y);
10830       }
10831     }
10832
10833     if (change->has_action && !handle_action_before_change)
10834       ExecuteCustomElementAction(x, y, element, page);
10835   }
10836 }
10837
10838 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10839                                               int trigger_element,
10840                                               int trigger_event,
10841                                               int trigger_player,
10842                                               int trigger_side,
10843                                               int trigger_page)
10844 {
10845   boolean change_done_any = FALSE;
10846   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10847   int i;
10848
10849   if (!(trigger_events[trigger_element][trigger_event]))
10850     return FALSE;
10851
10852   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10853
10854   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10855   {
10856     int element = EL_CUSTOM_START + i;
10857     boolean change_done = FALSE;
10858     int p;
10859
10860     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10861         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10862       continue;
10863
10864     for (p = 0; p < element_info[element].num_change_pages; p++)
10865     {
10866       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10867
10868       if (change->can_change_or_has_action &&
10869           change->has_event[trigger_event] &&
10870           change->trigger_side & trigger_side &&
10871           change->trigger_player & trigger_player &&
10872           change->trigger_page & trigger_page_bits &&
10873           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10874       {
10875         change->actual_trigger_element = trigger_element;
10876         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10877         change->actual_trigger_player_bits = trigger_player;
10878         change->actual_trigger_side = trigger_side;
10879         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10880         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10881
10882         if ((change->can_change && !change_done) || change->has_action)
10883         {
10884           int x, y;
10885
10886           SCAN_PLAYFIELD(x, y)
10887           {
10888             if (Tile[x][y] == element)
10889             {
10890               if (change->can_change && !change_done)
10891               {
10892                 // if element already changed in this frame, not only prevent
10893                 // another element change (checked in ChangeElement()), but
10894                 // also prevent additional element actions for this element
10895
10896                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10897                     !level.use_action_after_change_bug)
10898                   continue;
10899
10900                 ChangeDelay[x][y] = 1;
10901                 ChangeEvent[x][y] = trigger_event;
10902
10903                 HandleElementChange(x, y, p);
10904               }
10905               else if (change->has_action)
10906               {
10907                 // if element already changed in this frame, not only prevent
10908                 // another element change (checked in ChangeElement()), but
10909                 // also prevent additional element actions for this element
10910
10911                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10912                     !level.use_action_after_change_bug)
10913                   continue;
10914
10915                 ExecuteCustomElementAction(x, y, element, p);
10916                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10917               }
10918             }
10919           }
10920
10921           if (change->can_change)
10922           {
10923             change_done = TRUE;
10924             change_done_any = TRUE;
10925           }
10926         }
10927       }
10928     }
10929   }
10930
10931   RECURSION_LOOP_DETECTION_END();
10932
10933   return change_done_any;
10934 }
10935
10936 static boolean CheckElementChangeExt(int x, int y,
10937                                      int element,
10938                                      int trigger_element,
10939                                      int trigger_event,
10940                                      int trigger_player,
10941                                      int trigger_side)
10942 {
10943   boolean change_done = FALSE;
10944   int p;
10945
10946   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10947       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10948     return FALSE;
10949
10950   if (Tile[x][y] == EL_BLOCKED)
10951   {
10952     Blocked2Moving(x, y, &x, &y);
10953     element = Tile[x][y];
10954   }
10955
10956   // check if element has already changed or is about to change after moving
10957   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10958        Tile[x][y] != element) ||
10959
10960       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10961        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10962         ChangePage[x][y] != -1)))
10963     return FALSE;
10964
10965   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10966
10967   for (p = 0; p < element_info[element].num_change_pages; p++)
10968   {
10969     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10970
10971     /* check trigger element for all events where the element that is checked
10972        for changing interacts with a directly adjacent element -- this is
10973        different to element changes that affect other elements to change on the
10974        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10975     boolean check_trigger_element =
10976       (trigger_event == CE_TOUCHING_X ||
10977        trigger_event == CE_HITTING_X ||
10978        trigger_event == CE_HIT_BY_X ||
10979        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
10980
10981     if (change->can_change_or_has_action &&
10982         change->has_event[trigger_event] &&
10983         change->trigger_side & trigger_side &&
10984         change->trigger_player & trigger_player &&
10985         (!check_trigger_element ||
10986          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10987     {
10988       change->actual_trigger_element = trigger_element;
10989       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10990       change->actual_trigger_player_bits = trigger_player;
10991       change->actual_trigger_side = trigger_side;
10992       change->actual_trigger_ce_value = CustomValue[x][y];
10993       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10994
10995       // special case: trigger element not at (x,y) position for some events
10996       if (check_trigger_element)
10997       {
10998         static struct
10999         {
11000           int dx, dy;
11001         } move_xy[] =
11002           {
11003             {  0,  0 },
11004             { -1,  0 },
11005             { +1,  0 },
11006             {  0,  0 },
11007             {  0, -1 },
11008             {  0,  0 }, { 0, 0 }, { 0, 0 },
11009             {  0, +1 }
11010           };
11011
11012         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11013         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11014
11015         change->actual_trigger_ce_value = CustomValue[xx][yy];
11016         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11017       }
11018
11019       if (change->can_change && !change_done)
11020       {
11021         ChangeDelay[x][y] = 1;
11022         ChangeEvent[x][y] = trigger_event;
11023
11024         HandleElementChange(x, y, p);
11025
11026         change_done = TRUE;
11027       }
11028       else if (change->has_action)
11029       {
11030         ExecuteCustomElementAction(x, y, element, p);
11031         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11032       }
11033     }
11034   }
11035
11036   RECURSION_LOOP_DETECTION_END();
11037
11038   return change_done;
11039 }
11040
11041 static void PlayPlayerSound(struct PlayerInfo *player)
11042 {
11043   int jx = player->jx, jy = player->jy;
11044   int sound_element = player->artwork_element;
11045   int last_action = player->last_action_waiting;
11046   int action = player->action_waiting;
11047
11048   if (player->is_waiting)
11049   {
11050     if (action != last_action)
11051       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11052     else
11053       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11054   }
11055   else
11056   {
11057     if (action != last_action)
11058       StopSound(element_info[sound_element].sound[last_action]);
11059
11060     if (last_action == ACTION_SLEEPING)
11061       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11062   }
11063 }
11064
11065 static void PlayAllPlayersSound(void)
11066 {
11067   int i;
11068
11069   for (i = 0; i < MAX_PLAYERS; i++)
11070     if (stored_player[i].active)
11071       PlayPlayerSound(&stored_player[i]);
11072 }
11073
11074 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11075 {
11076   boolean last_waiting = player->is_waiting;
11077   int move_dir = player->MovDir;
11078
11079   player->dir_waiting = move_dir;
11080   player->last_action_waiting = player->action_waiting;
11081
11082   if (is_waiting)
11083   {
11084     if (!last_waiting)          // not waiting -> waiting
11085     {
11086       player->is_waiting = TRUE;
11087
11088       player->frame_counter_bored =
11089         FrameCounter +
11090         game.player_boring_delay_fixed +
11091         GetSimpleRandom(game.player_boring_delay_random);
11092       player->frame_counter_sleeping =
11093         FrameCounter +
11094         game.player_sleeping_delay_fixed +
11095         GetSimpleRandom(game.player_sleeping_delay_random);
11096
11097       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11098     }
11099
11100     if (game.player_sleeping_delay_fixed +
11101         game.player_sleeping_delay_random > 0 &&
11102         player->anim_delay_counter == 0 &&
11103         player->post_delay_counter == 0 &&
11104         FrameCounter >= player->frame_counter_sleeping)
11105       player->is_sleeping = TRUE;
11106     else if (game.player_boring_delay_fixed +
11107              game.player_boring_delay_random > 0 &&
11108              FrameCounter >= player->frame_counter_bored)
11109       player->is_bored = TRUE;
11110
11111     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11112                               player->is_bored ? ACTION_BORING :
11113                               ACTION_WAITING);
11114
11115     if (player->is_sleeping && player->use_murphy)
11116     {
11117       // special case for sleeping Murphy when leaning against non-free tile
11118
11119       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11120           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11121            !IS_MOVING(player->jx - 1, player->jy)))
11122         move_dir = MV_LEFT;
11123       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11124                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11125                 !IS_MOVING(player->jx + 1, player->jy)))
11126         move_dir = MV_RIGHT;
11127       else
11128         player->is_sleeping = FALSE;
11129
11130       player->dir_waiting = move_dir;
11131     }
11132
11133     if (player->is_sleeping)
11134     {
11135       if (player->num_special_action_sleeping > 0)
11136       {
11137         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11138         {
11139           int last_special_action = player->special_action_sleeping;
11140           int num_special_action = player->num_special_action_sleeping;
11141           int special_action =
11142             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11143              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11144              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11145              last_special_action + 1 : ACTION_SLEEPING);
11146           int special_graphic =
11147             el_act_dir2img(player->artwork_element, special_action, move_dir);
11148
11149           player->anim_delay_counter =
11150             graphic_info[special_graphic].anim_delay_fixed +
11151             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11152           player->post_delay_counter =
11153             graphic_info[special_graphic].post_delay_fixed +
11154             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11155
11156           player->special_action_sleeping = special_action;
11157         }
11158
11159         if (player->anim_delay_counter > 0)
11160         {
11161           player->action_waiting = player->special_action_sleeping;
11162           player->anim_delay_counter--;
11163         }
11164         else if (player->post_delay_counter > 0)
11165         {
11166           player->post_delay_counter--;
11167         }
11168       }
11169     }
11170     else if (player->is_bored)
11171     {
11172       if (player->num_special_action_bored > 0)
11173       {
11174         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11175         {
11176           int special_action =
11177             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
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_bored = special_action;
11189         }
11190
11191         if (player->anim_delay_counter > 0)
11192         {
11193           player->action_waiting = player->special_action_bored;
11194           player->anim_delay_counter--;
11195         }
11196         else if (player->post_delay_counter > 0)
11197         {
11198           player->post_delay_counter--;
11199         }
11200       }
11201     }
11202   }
11203   else if (last_waiting)        // waiting -> not waiting
11204   {
11205     player->is_waiting = FALSE;
11206     player->is_bored = FALSE;
11207     player->is_sleeping = FALSE;
11208
11209     player->frame_counter_bored = -1;
11210     player->frame_counter_sleeping = -1;
11211
11212     player->anim_delay_counter = 0;
11213     player->post_delay_counter = 0;
11214
11215     player->dir_waiting = player->MovDir;
11216     player->action_waiting = ACTION_DEFAULT;
11217
11218     player->special_action_bored = ACTION_DEFAULT;
11219     player->special_action_sleeping = ACTION_DEFAULT;
11220   }
11221 }
11222
11223 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11224 {
11225   if ((!player->is_moving  && player->was_moving) ||
11226       (player->MovPos == 0 && player->was_moving) ||
11227       (player->is_snapping && !player->was_snapping) ||
11228       (player->is_dropping && !player->was_dropping))
11229   {
11230     if (!CheckSaveEngineSnapshotToList())
11231       return;
11232
11233     player->was_moving = FALSE;
11234     player->was_snapping = TRUE;
11235     player->was_dropping = TRUE;
11236   }
11237   else
11238   {
11239     if (player->is_moving)
11240       player->was_moving = TRUE;
11241
11242     if (!player->is_snapping)
11243       player->was_snapping = FALSE;
11244
11245     if (!player->is_dropping)
11246       player->was_dropping = FALSE;
11247   }
11248
11249   static struct MouseActionInfo mouse_action_last = { 0 };
11250   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11251   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11252
11253   if (new_released)
11254     CheckSaveEngineSnapshotToList();
11255
11256   mouse_action_last = mouse_action;
11257 }
11258
11259 static void CheckSingleStepMode(struct PlayerInfo *player)
11260 {
11261   if (tape.single_step && tape.recording && !tape.pausing)
11262   {
11263     /* as it is called "single step mode", just return to pause mode when the
11264        player stopped moving after one tile (or never starts moving at all) */
11265     if (!player->is_moving &&
11266         !player->is_pushing &&
11267         !player->is_dropping_pressed &&
11268         !player->effective_mouse_action.button)
11269       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11270   }
11271
11272   CheckSaveEngineSnapshot(player);
11273 }
11274
11275 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11276 {
11277   int left      = player_action & JOY_LEFT;
11278   int right     = player_action & JOY_RIGHT;
11279   int up        = player_action & JOY_UP;
11280   int down      = player_action & JOY_DOWN;
11281   int button1   = player_action & JOY_BUTTON_1;
11282   int button2   = player_action & JOY_BUTTON_2;
11283   int dx        = (left ? -1 : right ? 1 : 0);
11284   int dy        = (up   ? -1 : down  ? 1 : 0);
11285
11286   if (!player->active || tape.pausing)
11287     return 0;
11288
11289   if (player_action)
11290   {
11291     if (button1)
11292       SnapField(player, dx, dy);
11293     else
11294     {
11295       if (button2)
11296         DropElement(player);
11297
11298       MovePlayer(player, dx, dy);
11299     }
11300
11301     CheckSingleStepMode(player);
11302
11303     SetPlayerWaiting(player, FALSE);
11304
11305     return player_action;
11306   }
11307   else
11308   {
11309     // no actions for this player (no input at player's configured device)
11310
11311     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11312     SnapField(player, 0, 0);
11313     CheckGravityMovementWhenNotMoving(player);
11314
11315     if (player->MovPos == 0)
11316       SetPlayerWaiting(player, TRUE);
11317
11318     if (player->MovPos == 0)    // needed for tape.playing
11319       player->is_moving = FALSE;
11320
11321     player->is_dropping = FALSE;
11322     player->is_dropping_pressed = FALSE;
11323     player->drop_pressed_delay = 0;
11324
11325     CheckSingleStepMode(player);
11326
11327     return 0;
11328   }
11329 }
11330
11331 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11332                                          byte *tape_action)
11333 {
11334   if (!tape.use_mouse_actions)
11335     return;
11336
11337   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11338   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11339   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11340 }
11341
11342 static void SetTapeActionFromMouseAction(byte *tape_action,
11343                                          struct MouseActionInfo *mouse_action)
11344 {
11345   if (!tape.use_mouse_actions)
11346     return;
11347
11348   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11349   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11350   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11351 }
11352
11353 static void CheckLevelSolved(void)
11354 {
11355   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11356   {
11357     if (game_em.level_solved &&
11358         !game_em.game_over)                             // game won
11359     {
11360       LevelSolved();
11361
11362       game_em.game_over = TRUE;
11363
11364       game.all_players_gone = TRUE;
11365     }
11366
11367     if (game_em.game_over)                              // game lost
11368       game.all_players_gone = TRUE;
11369   }
11370   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11371   {
11372     if (game_sp.level_solved &&
11373         !game_sp.game_over)                             // game won
11374     {
11375       LevelSolved();
11376
11377       game_sp.game_over = TRUE;
11378
11379       game.all_players_gone = TRUE;
11380     }
11381
11382     if (game_sp.game_over)                              // game lost
11383       game.all_players_gone = TRUE;
11384   }
11385   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11386   {
11387     if (game_mm.level_solved &&
11388         !game_mm.game_over)                             // game won
11389     {
11390       LevelSolved();
11391
11392       game_mm.game_over = TRUE;
11393
11394       game.all_players_gone = TRUE;
11395     }
11396
11397     if (game_mm.game_over)                              // game lost
11398       game.all_players_gone = TRUE;
11399   }
11400 }
11401
11402 static void CheckLevelTime(void)
11403 {
11404   int i;
11405
11406   if (TimeFrames >= FRAMES_PER_SECOND)
11407   {
11408     TimeFrames = 0;
11409     TapeTime++;
11410
11411     for (i = 0; i < MAX_PLAYERS; i++)
11412     {
11413       struct PlayerInfo *player = &stored_player[i];
11414
11415       if (SHIELD_ON(player))
11416       {
11417         player->shield_normal_time_left--;
11418
11419         if (player->shield_deadly_time_left > 0)
11420           player->shield_deadly_time_left--;
11421       }
11422     }
11423
11424     if (!game.LevelSolved && !level.use_step_counter)
11425     {
11426       TimePlayed++;
11427
11428       if (TimeLeft > 0)
11429       {
11430         TimeLeft--;
11431
11432         if (TimeLeft <= 10 && setup.time_limit)
11433           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11434
11435         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11436            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11437
11438         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11439
11440         if (!TimeLeft && setup.time_limit)
11441         {
11442           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11443             game_em.lev->killed_out_of_time = TRUE;
11444           else
11445             for (i = 0; i < MAX_PLAYERS; i++)
11446               KillPlayer(&stored_player[i]);
11447         }
11448       }
11449       else if (game.no_time_limit && !game.all_players_gone)
11450       {
11451         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11452       }
11453
11454       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11455     }
11456
11457     if (tape.recording || tape.playing)
11458       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11459   }
11460
11461   if (tape.recording || tape.playing)
11462     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11463
11464   UpdateAndDisplayGameControlValues();
11465 }
11466
11467 void AdvanceFrameAndPlayerCounters(int player_nr)
11468 {
11469   int i;
11470
11471   // advance frame counters (global frame counter and time frame counter)
11472   FrameCounter++;
11473   TimeFrames++;
11474
11475   // advance player counters (counters for move delay, move animation etc.)
11476   for (i = 0; i < MAX_PLAYERS; i++)
11477   {
11478     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11479     int move_delay_value = stored_player[i].move_delay_value;
11480     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11481
11482     if (!advance_player_counters)       // not all players may be affected
11483       continue;
11484
11485     if (move_frames == 0)       // less than one move per game frame
11486     {
11487       int stepsize = TILEX / move_delay_value;
11488       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11489       int count = (stored_player[i].is_moving ?
11490                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11491
11492       if (count % delay == 0)
11493         move_frames = 1;
11494     }
11495
11496     stored_player[i].Frame += move_frames;
11497
11498     if (stored_player[i].MovPos != 0)
11499       stored_player[i].StepFrame += move_frames;
11500
11501     if (stored_player[i].move_delay > 0)
11502       stored_player[i].move_delay--;
11503
11504     // due to bugs in previous versions, counter must count up, not down
11505     if (stored_player[i].push_delay != -1)
11506       stored_player[i].push_delay++;
11507
11508     if (stored_player[i].drop_delay > 0)
11509       stored_player[i].drop_delay--;
11510
11511     if (stored_player[i].is_dropping_pressed)
11512       stored_player[i].drop_pressed_delay++;
11513   }
11514 }
11515
11516 void StartGameActions(boolean init_network_game, boolean record_tape,
11517                       int random_seed)
11518 {
11519   unsigned int new_random_seed = InitRND(random_seed);
11520
11521   if (record_tape)
11522     TapeStartRecording(new_random_seed);
11523
11524   if (init_network_game)
11525   {
11526     SendToServer_LevelFile();
11527     SendToServer_StartPlaying();
11528
11529     return;
11530   }
11531
11532   InitGame();
11533 }
11534
11535 static void GameActionsExt(void)
11536 {
11537 #if 0
11538   static unsigned int game_frame_delay = 0;
11539 #endif
11540   unsigned int game_frame_delay_value;
11541   byte *recorded_player_action;
11542   byte summarized_player_action = 0;
11543   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11544   int i;
11545
11546   // detect endless loops, caused by custom element programming
11547   if (recursion_loop_detected && recursion_loop_depth == 0)
11548   {
11549     char *message = getStringCat3("Internal Error! Element ",
11550                                   EL_NAME(recursion_loop_element),
11551                                   " caused endless loop! Quit the game?");
11552
11553     Warn("element '%s' caused endless loop in game engine",
11554          EL_NAME(recursion_loop_element));
11555
11556     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11557
11558     recursion_loop_detected = FALSE;    // if game should be continued
11559
11560     free(message);
11561
11562     return;
11563   }
11564
11565   if (game.restart_level)
11566     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11567
11568   CheckLevelSolved();
11569
11570   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11571     GameWon();
11572
11573   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11574     TapeStop();
11575
11576   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11577     return;
11578
11579   game_frame_delay_value =
11580     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11581
11582   if (tape.playing && tape.warp_forward && !tape.pausing)
11583     game_frame_delay_value = 0;
11584
11585   SetVideoFrameDelay(game_frame_delay_value);
11586
11587   // (de)activate virtual buttons depending on current game status
11588   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11589   {
11590     if (game.all_players_gone)  // if no players there to be controlled anymore
11591       SetOverlayActive(FALSE);
11592     else if (!tape.playing)     // if game continues after tape stopped playing
11593       SetOverlayActive(TRUE);
11594   }
11595
11596 #if 0
11597 #if 0
11598   // ---------- main game synchronization point ----------
11599
11600   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11601
11602   Debug("game:playing:skip", "skip == %d", skip);
11603
11604 #else
11605   // ---------- main game synchronization point ----------
11606
11607   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11608 #endif
11609 #endif
11610
11611   if (network_playing && !network_player_action_received)
11612   {
11613     // try to get network player actions in time
11614
11615     // last chance to get network player actions without main loop delay
11616     HandleNetworking();
11617
11618     // game was quit by network peer
11619     if (game_status != GAME_MODE_PLAYING)
11620       return;
11621
11622     // check if network player actions still missing and game still running
11623     if (!network_player_action_received && !checkGameEnded())
11624       return;           // failed to get network player actions in time
11625
11626     // do not yet reset "network_player_action_received" (for tape.pausing)
11627   }
11628
11629   if (tape.pausing)
11630     return;
11631
11632   // at this point we know that we really continue executing the game
11633
11634   network_player_action_received = FALSE;
11635
11636   // when playing tape, read previously recorded player input from tape data
11637   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11638
11639   local_player->effective_mouse_action = local_player->mouse_action;
11640
11641   if (recorded_player_action != NULL)
11642     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11643                                  recorded_player_action);
11644
11645   // TapePlayAction() may return NULL when toggling to "pause before death"
11646   if (tape.pausing)
11647     return;
11648
11649   if (tape.set_centered_player)
11650   {
11651     game.centered_player_nr_next = tape.centered_player_nr_next;
11652     game.set_centered_player = TRUE;
11653   }
11654
11655   for (i = 0; i < MAX_PLAYERS; i++)
11656   {
11657     summarized_player_action |= stored_player[i].action;
11658
11659     if (!network_playing && (game.team_mode || tape.playing))
11660       stored_player[i].effective_action = stored_player[i].action;
11661   }
11662
11663   if (network_playing && !checkGameEnded())
11664     SendToServer_MovePlayer(summarized_player_action);
11665
11666   // summarize all actions at local players mapped input device position
11667   // (this allows using different input devices in single player mode)
11668   if (!network.enabled && !game.team_mode)
11669     stored_player[map_player_action[local_player->index_nr]].effective_action =
11670       summarized_player_action;
11671
11672   // summarize all actions at centered player in local team mode
11673   if (tape.recording &&
11674       setup.team_mode && !network.enabled &&
11675       setup.input_on_focus &&
11676       game.centered_player_nr != -1)
11677   {
11678     for (i = 0; i < MAX_PLAYERS; i++)
11679       stored_player[map_player_action[i]].effective_action =
11680         (i == game.centered_player_nr ? summarized_player_action : 0);
11681   }
11682
11683   if (recorded_player_action != NULL)
11684     for (i = 0; i < MAX_PLAYERS; i++)
11685       stored_player[i].effective_action = recorded_player_action[i];
11686
11687   for (i = 0; i < MAX_PLAYERS; i++)
11688   {
11689     tape_action[i] = stored_player[i].effective_action;
11690
11691     /* (this may happen in the RND game engine if a player was not present on
11692        the playfield on level start, but appeared later from a custom element */
11693     if (setup.team_mode &&
11694         tape.recording &&
11695         tape_action[i] &&
11696         !tape.player_participates[i])
11697       tape.player_participates[i] = TRUE;
11698   }
11699
11700   SetTapeActionFromMouseAction(tape_action,
11701                                &local_player->effective_mouse_action);
11702
11703   // only record actions from input devices, but not programmed actions
11704   if (tape.recording)
11705     TapeRecordAction(tape_action);
11706
11707   // remember if game was played (especially after tape stopped playing)
11708   if (!tape.playing && summarized_player_action)
11709     game.GamePlayed = TRUE;
11710
11711 #if USE_NEW_PLAYER_ASSIGNMENTS
11712   // !!! also map player actions in single player mode !!!
11713   // if (game.team_mode)
11714   if (1)
11715   {
11716     byte mapped_action[MAX_PLAYERS];
11717
11718 #if DEBUG_PLAYER_ACTIONS
11719     for (i = 0; i < MAX_PLAYERS; i++)
11720       DebugContinued("", "%d, ", stored_player[i].effective_action);
11721 #endif
11722
11723     for (i = 0; i < MAX_PLAYERS; i++)
11724       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11725
11726     for (i = 0; i < MAX_PLAYERS; i++)
11727       stored_player[i].effective_action = mapped_action[i];
11728
11729 #if DEBUG_PLAYER_ACTIONS
11730     DebugContinued("", "=> ");
11731     for (i = 0; i < MAX_PLAYERS; i++)
11732       DebugContinued("", "%d, ", stored_player[i].effective_action);
11733     DebugContinued("game:playing:player", "\n");
11734 #endif
11735   }
11736 #if DEBUG_PLAYER_ACTIONS
11737   else
11738   {
11739     for (i = 0; i < MAX_PLAYERS; i++)
11740       DebugContinued("", "%d, ", stored_player[i].effective_action);
11741     DebugContinued("game:playing:player", "\n");
11742   }
11743 #endif
11744 #endif
11745
11746   for (i = 0; i < MAX_PLAYERS; i++)
11747   {
11748     // allow engine snapshot in case of changed movement attempt
11749     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11750         (stored_player[i].effective_action & KEY_MOTION))
11751       game.snapshot.changed_action = TRUE;
11752
11753     // allow engine snapshot in case of snapping/dropping attempt
11754     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11755         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11756       game.snapshot.changed_action = TRUE;
11757
11758     game.snapshot.last_action[i] = stored_player[i].effective_action;
11759   }
11760
11761   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11762   {
11763     GameActions_EM_Main();
11764   }
11765   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11766   {
11767     GameActions_SP_Main();
11768   }
11769   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11770   {
11771     GameActions_MM_Main();
11772   }
11773   else
11774   {
11775     GameActions_RND_Main();
11776   }
11777
11778   BlitScreenToBitmap(backbuffer);
11779
11780   CheckLevelSolved();
11781   CheckLevelTime();
11782
11783   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11784
11785   if (global.show_frames_per_second)
11786   {
11787     static unsigned int fps_counter = 0;
11788     static int fps_frames = 0;
11789     unsigned int fps_delay_ms = Counter() - fps_counter;
11790
11791     fps_frames++;
11792
11793     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11794     {
11795       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11796
11797       fps_frames = 0;
11798       fps_counter = Counter();
11799
11800       // always draw FPS to screen after FPS value was updated
11801       redraw_mask |= REDRAW_FPS;
11802     }
11803
11804     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11805     if (GetDrawDeactivationMask() == REDRAW_NONE)
11806       redraw_mask |= REDRAW_FPS;
11807   }
11808 }
11809
11810 static void GameActions_CheckSaveEngineSnapshot(void)
11811 {
11812   if (!game.snapshot.save_snapshot)
11813     return;
11814
11815   // clear flag for saving snapshot _before_ saving snapshot
11816   game.snapshot.save_snapshot = FALSE;
11817
11818   SaveEngineSnapshotToList();
11819 }
11820
11821 void GameActions(void)
11822 {
11823   GameActionsExt();
11824
11825   GameActions_CheckSaveEngineSnapshot();
11826 }
11827
11828 void GameActions_EM_Main(void)
11829 {
11830   byte effective_action[MAX_PLAYERS];
11831   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11832   int i;
11833
11834   for (i = 0; i < MAX_PLAYERS; i++)
11835     effective_action[i] = stored_player[i].effective_action;
11836
11837   GameActions_EM(effective_action, warp_mode);
11838 }
11839
11840 void GameActions_SP_Main(void)
11841 {
11842   byte effective_action[MAX_PLAYERS];
11843   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11844   int i;
11845
11846   for (i = 0; i < MAX_PLAYERS; i++)
11847     effective_action[i] = stored_player[i].effective_action;
11848
11849   GameActions_SP(effective_action, warp_mode);
11850
11851   for (i = 0; i < MAX_PLAYERS; i++)
11852   {
11853     if (stored_player[i].force_dropping)
11854       stored_player[i].action |= KEY_BUTTON_DROP;
11855
11856     stored_player[i].force_dropping = FALSE;
11857   }
11858 }
11859
11860 void GameActions_MM_Main(void)
11861 {
11862   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11863
11864   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11865 }
11866
11867 void GameActions_RND_Main(void)
11868 {
11869   GameActions_RND();
11870 }
11871
11872 void GameActions_RND(void)
11873 {
11874   static struct MouseActionInfo mouse_action_last = { 0 };
11875   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
11876   int magic_wall_x = 0, magic_wall_y = 0;
11877   int i, x, y, element, graphic, last_gfx_frame;
11878
11879   InitPlayfieldScanModeVars();
11880
11881   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11882   {
11883     SCAN_PLAYFIELD(x, y)
11884     {
11885       ChangeCount[x][y] = 0;
11886       ChangeEvent[x][y] = -1;
11887     }
11888   }
11889
11890   if (game.set_centered_player)
11891   {
11892     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11893
11894     // switching to "all players" only possible if all players fit to screen
11895     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11896     {
11897       game.centered_player_nr_next = game.centered_player_nr;
11898       game.set_centered_player = FALSE;
11899     }
11900
11901     // do not switch focus to non-existing (or non-active) player
11902     if (game.centered_player_nr_next >= 0 &&
11903         !stored_player[game.centered_player_nr_next].active)
11904     {
11905       game.centered_player_nr_next = game.centered_player_nr;
11906       game.set_centered_player = FALSE;
11907     }
11908   }
11909
11910   if (game.set_centered_player &&
11911       ScreenMovPos == 0)        // screen currently aligned at tile position
11912   {
11913     int sx, sy;
11914
11915     if (game.centered_player_nr_next == -1)
11916     {
11917       setScreenCenteredToAllPlayers(&sx, &sy);
11918     }
11919     else
11920     {
11921       sx = stored_player[game.centered_player_nr_next].jx;
11922       sy = stored_player[game.centered_player_nr_next].jy;
11923     }
11924
11925     game.centered_player_nr = game.centered_player_nr_next;
11926     game.set_centered_player = FALSE;
11927
11928     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11929     DrawGameDoorValues();
11930   }
11931
11932   for (i = 0; i < MAX_PLAYERS; i++)
11933   {
11934     int actual_player_action = stored_player[i].effective_action;
11935
11936 #if 1
11937     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11938        - rnd_equinox_tetrachloride 048
11939        - rnd_equinox_tetrachloride_ii 096
11940        - rnd_emanuel_schmieg 002
11941        - doctor_sloan_ww 001, 020
11942     */
11943     if (stored_player[i].MovPos == 0)
11944       CheckGravityMovement(&stored_player[i]);
11945 #endif
11946
11947     // overwrite programmed action with tape action
11948     if (stored_player[i].programmed_action)
11949       actual_player_action = stored_player[i].programmed_action;
11950
11951     PlayerActions(&stored_player[i], actual_player_action);
11952
11953     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11954   }
11955
11956   ScrollScreen(NULL, SCROLL_GO_ON);
11957
11958   /* for backwards compatibility, the following code emulates a fixed bug that
11959      occured when pushing elements (causing elements that just made their last
11960      pushing step to already (if possible) make their first falling step in the
11961      same game frame, which is bad); this code is also needed to use the famous
11962      "spring push bug" which is used in older levels and might be wanted to be
11963      used also in newer levels, but in this case the buggy pushing code is only
11964      affecting the "spring" element and no other elements */
11965
11966   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11967   {
11968     for (i = 0; i < MAX_PLAYERS; i++)
11969     {
11970       struct PlayerInfo *player = &stored_player[i];
11971       int x = player->jx;
11972       int y = player->jy;
11973
11974       if (player->active && player->is_pushing && player->is_moving &&
11975           IS_MOVING(x, y) &&
11976           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11977            Tile[x][y] == EL_SPRING))
11978       {
11979         ContinueMoving(x, y);
11980
11981         // continue moving after pushing (this is actually a bug)
11982         if (!IS_MOVING(x, y))
11983           Stop[x][y] = FALSE;
11984       }
11985     }
11986   }
11987
11988   SCAN_PLAYFIELD(x, y)
11989   {
11990     Last[x][y] = Tile[x][y];
11991
11992     ChangeCount[x][y] = 0;
11993     ChangeEvent[x][y] = -1;
11994
11995     // this must be handled before main playfield loop
11996     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
11997     {
11998       MovDelay[x][y]--;
11999       if (MovDelay[x][y] <= 0)
12000         RemoveField(x, y);
12001     }
12002
12003     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12004     {
12005       MovDelay[x][y]--;
12006       if (MovDelay[x][y] <= 0)
12007       {
12008         RemoveField(x, y);
12009         TEST_DrawLevelField(x, y);
12010
12011         TestIfElementTouchesCustomElement(x, y);        // for empty space
12012       }
12013     }
12014
12015 #if DEBUG
12016     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12017     {
12018       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12019             x, y);
12020       Debug("game:playing:GameActions_RND", "This should never happen!");
12021
12022       ChangePage[x][y] = -1;
12023     }
12024 #endif
12025
12026     Stop[x][y] = FALSE;
12027     if (WasJustMoving[x][y] > 0)
12028       WasJustMoving[x][y]--;
12029     if (WasJustFalling[x][y] > 0)
12030       WasJustFalling[x][y]--;
12031     if (CheckCollision[x][y] > 0)
12032       CheckCollision[x][y]--;
12033     if (CheckImpact[x][y] > 0)
12034       CheckImpact[x][y]--;
12035
12036     GfxFrame[x][y]++;
12037
12038     /* reset finished pushing action (not done in ContinueMoving() to allow
12039        continuous pushing animation for elements with zero push delay) */
12040     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12041     {
12042       ResetGfxAnimation(x, y);
12043       TEST_DrawLevelField(x, y);
12044     }
12045
12046 #if DEBUG
12047     if (IS_BLOCKED(x, y))
12048     {
12049       int oldx, oldy;
12050
12051       Blocked2Moving(x, y, &oldx, &oldy);
12052       if (!IS_MOVING(oldx, oldy))
12053       {
12054         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12055         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12056         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12057         Debug("game:playing:GameActions_RND", "This should never happen!");
12058       }
12059     }
12060 #endif
12061   }
12062
12063   if (mouse_action.button)
12064   {
12065     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12066
12067     x = mouse_action.lx;
12068     y = mouse_action.ly;
12069     element = Tile[x][y];
12070
12071     if (new_button)
12072     {
12073       CheckElementChange(x, y, element, EL_UNDEFINED, CE_CLICKED_BY_MOUSE);
12074       CheckTriggeredElementChange(x, y, element, CE_MOUSE_CLICKED_ON_X);
12075     }
12076
12077     CheckElementChange(x, y, element, EL_UNDEFINED, CE_PRESSED_BY_MOUSE);
12078     CheckTriggeredElementChange(x, y, element, CE_MOUSE_PRESSED_ON_X);
12079   }
12080
12081   SCAN_PLAYFIELD(x, y)
12082   {
12083     element = Tile[x][y];
12084     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12085     last_gfx_frame = GfxFrame[x][y];
12086
12087     ResetGfxFrame(x, y);
12088
12089     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12090       DrawLevelGraphicAnimation(x, y, graphic);
12091
12092     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12093         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12094       ResetRandomAnimationValue(x, y);
12095
12096     SetRandomAnimationValue(x, y);
12097
12098     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12099
12100     if (IS_INACTIVE(element))
12101     {
12102       if (IS_ANIMATED(graphic))
12103         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12104
12105       continue;
12106     }
12107
12108     // this may take place after moving, so 'element' may have changed
12109     if (IS_CHANGING(x, y) &&
12110         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12111     {
12112       int page = element_info[element].event_page_nr[CE_DELAY];
12113
12114       HandleElementChange(x, y, page);
12115
12116       element = Tile[x][y];
12117       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12118     }
12119
12120     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12121     {
12122       StartMoving(x, y);
12123
12124       element = Tile[x][y];
12125       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12126
12127       if (IS_ANIMATED(graphic) &&
12128           !IS_MOVING(x, y) &&
12129           !Stop[x][y])
12130         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12131
12132       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12133         TEST_DrawTwinkleOnField(x, y);
12134     }
12135     else if (element == EL_ACID)
12136     {
12137       if (!Stop[x][y])
12138         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12139     }
12140     else if ((element == EL_EXIT_OPEN ||
12141               element == EL_EM_EXIT_OPEN ||
12142               element == EL_SP_EXIT_OPEN ||
12143               element == EL_STEEL_EXIT_OPEN ||
12144               element == EL_EM_STEEL_EXIT_OPEN ||
12145               element == EL_SP_TERMINAL ||
12146               element == EL_SP_TERMINAL_ACTIVE ||
12147               element == EL_EXTRA_TIME ||
12148               element == EL_SHIELD_NORMAL ||
12149               element == EL_SHIELD_DEADLY) &&
12150              IS_ANIMATED(graphic))
12151       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12152     else if (IS_MOVING(x, y))
12153       ContinueMoving(x, y);
12154     else if (IS_ACTIVE_BOMB(element))
12155       CheckDynamite(x, y);
12156     else if (element == EL_AMOEBA_GROWING)
12157       AmoebaGrowing(x, y);
12158     else if (element == EL_AMOEBA_SHRINKING)
12159       AmoebaShrinking(x, y);
12160
12161 #if !USE_NEW_AMOEBA_CODE
12162     else if (IS_AMOEBALIVE(element))
12163       AmoebaReproduce(x, y);
12164 #endif
12165
12166     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12167       Life(x, y);
12168     else if (element == EL_EXIT_CLOSED)
12169       CheckExit(x, y);
12170     else if (element == EL_EM_EXIT_CLOSED)
12171       CheckExitEM(x, y);
12172     else if (element == EL_STEEL_EXIT_CLOSED)
12173       CheckExitSteel(x, y);
12174     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12175       CheckExitSteelEM(x, y);
12176     else if (element == EL_SP_EXIT_CLOSED)
12177       CheckExitSP(x, y);
12178     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12179              element == EL_EXPANDABLE_STEELWALL_GROWING)
12180       MauerWaechst(x, y);
12181     else if (element == EL_EXPANDABLE_WALL ||
12182              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12183              element == EL_EXPANDABLE_WALL_VERTICAL ||
12184              element == EL_EXPANDABLE_WALL_ANY ||
12185              element == EL_BD_EXPANDABLE_WALL)
12186       MauerAbleger(x, y);
12187     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12188              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12189              element == EL_EXPANDABLE_STEELWALL_ANY)
12190       MauerAblegerStahl(x, y);
12191     else if (element == EL_FLAMES)
12192       CheckForDragon(x, y);
12193     else if (element == EL_EXPLOSION)
12194       ; // drawing of correct explosion animation is handled separately
12195     else if (element == EL_ELEMENT_SNAPPING ||
12196              element == EL_DIAGONAL_SHRINKING ||
12197              element == EL_DIAGONAL_GROWING)
12198     {
12199       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12200
12201       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12202     }
12203     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12204       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12205
12206     if (IS_BELT_ACTIVE(element))
12207       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12208
12209     if (game.magic_wall_active)
12210     {
12211       int jx = local_player->jx, jy = local_player->jy;
12212
12213       // play the element sound at the position nearest to the player
12214       if ((element == EL_MAGIC_WALL_FULL ||
12215            element == EL_MAGIC_WALL_ACTIVE ||
12216            element == EL_MAGIC_WALL_EMPTYING ||
12217            element == EL_BD_MAGIC_WALL_FULL ||
12218            element == EL_BD_MAGIC_WALL_ACTIVE ||
12219            element == EL_BD_MAGIC_WALL_EMPTYING ||
12220            element == EL_DC_MAGIC_WALL_FULL ||
12221            element == EL_DC_MAGIC_WALL_ACTIVE ||
12222            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12223           ABS(x - jx) + ABS(y - jy) <
12224           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12225       {
12226         magic_wall_x = x;
12227         magic_wall_y = y;
12228       }
12229     }
12230   }
12231
12232 #if USE_NEW_AMOEBA_CODE
12233   // new experimental amoeba growth stuff
12234   if (!(FrameCounter % 8))
12235   {
12236     static unsigned int random = 1684108901;
12237
12238     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12239     {
12240       x = RND(lev_fieldx);
12241       y = RND(lev_fieldy);
12242       element = Tile[x][y];
12243
12244       if (!IS_PLAYER(x,y) &&
12245           (element == EL_EMPTY ||
12246            CAN_GROW_INTO(element) ||
12247            element == EL_QUICKSAND_EMPTY ||
12248            element == EL_QUICKSAND_FAST_EMPTY ||
12249            element == EL_ACID_SPLASH_LEFT ||
12250            element == EL_ACID_SPLASH_RIGHT))
12251       {
12252         if ((IN_LEV_FIELD(x, y-1) && Tile[x][y-1] == EL_AMOEBA_WET) ||
12253             (IN_LEV_FIELD(x-1, y) && Tile[x-1][y] == EL_AMOEBA_WET) ||
12254             (IN_LEV_FIELD(x+1, y) && Tile[x+1][y] == EL_AMOEBA_WET) ||
12255             (IN_LEV_FIELD(x, y+1) && Tile[x][y+1] == EL_AMOEBA_WET))
12256           Tile[x][y] = EL_AMOEBA_DROP;
12257       }
12258
12259       random = random * 129 + 1;
12260     }
12261   }
12262 #endif
12263
12264   game.explosions_delayed = FALSE;
12265
12266   SCAN_PLAYFIELD(x, y)
12267   {
12268     element = Tile[x][y];
12269
12270     if (ExplodeField[x][y])
12271       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12272     else if (element == EL_EXPLOSION)
12273       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12274
12275     ExplodeField[x][y] = EX_TYPE_NONE;
12276   }
12277
12278   game.explosions_delayed = TRUE;
12279
12280   if (game.magic_wall_active)
12281   {
12282     if (!(game.magic_wall_time_left % 4))
12283     {
12284       int element = Tile[magic_wall_x][magic_wall_y];
12285
12286       if (element == EL_BD_MAGIC_WALL_FULL ||
12287           element == EL_BD_MAGIC_WALL_ACTIVE ||
12288           element == EL_BD_MAGIC_WALL_EMPTYING)
12289         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12290       else if (element == EL_DC_MAGIC_WALL_FULL ||
12291                element == EL_DC_MAGIC_WALL_ACTIVE ||
12292                element == EL_DC_MAGIC_WALL_EMPTYING)
12293         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12294       else
12295         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12296     }
12297
12298     if (game.magic_wall_time_left > 0)
12299     {
12300       game.magic_wall_time_left--;
12301
12302       if (!game.magic_wall_time_left)
12303       {
12304         SCAN_PLAYFIELD(x, y)
12305         {
12306           element = Tile[x][y];
12307
12308           if (element == EL_MAGIC_WALL_ACTIVE ||
12309               element == EL_MAGIC_WALL_FULL)
12310           {
12311             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12312             TEST_DrawLevelField(x, y);
12313           }
12314           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12315                    element == EL_BD_MAGIC_WALL_FULL)
12316           {
12317             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12318             TEST_DrawLevelField(x, y);
12319           }
12320           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12321                    element == EL_DC_MAGIC_WALL_FULL)
12322           {
12323             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12324             TEST_DrawLevelField(x, y);
12325           }
12326         }
12327
12328         game.magic_wall_active = FALSE;
12329       }
12330     }
12331   }
12332
12333   if (game.light_time_left > 0)
12334   {
12335     game.light_time_left--;
12336
12337     if (game.light_time_left == 0)
12338       RedrawAllLightSwitchesAndInvisibleElements();
12339   }
12340
12341   if (game.timegate_time_left > 0)
12342   {
12343     game.timegate_time_left--;
12344
12345     if (game.timegate_time_left == 0)
12346       CloseAllOpenTimegates();
12347   }
12348
12349   if (game.lenses_time_left > 0)
12350   {
12351     game.lenses_time_left--;
12352
12353     if (game.lenses_time_left == 0)
12354       RedrawAllInvisibleElementsForLenses();
12355   }
12356
12357   if (game.magnify_time_left > 0)
12358   {
12359     game.magnify_time_left--;
12360
12361     if (game.magnify_time_left == 0)
12362       RedrawAllInvisibleElementsForMagnifier();
12363   }
12364
12365   for (i = 0; i < MAX_PLAYERS; i++)
12366   {
12367     struct PlayerInfo *player = &stored_player[i];
12368
12369     if (SHIELD_ON(player))
12370     {
12371       if (player->shield_deadly_time_left)
12372         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12373       else if (player->shield_normal_time_left)
12374         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12375     }
12376   }
12377
12378 #if USE_DELAYED_GFX_REDRAW
12379   SCAN_PLAYFIELD(x, y)
12380   {
12381     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12382     {
12383       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12384          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12385
12386       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12387         DrawLevelField(x, y);
12388
12389       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12390         DrawLevelFieldCrumbled(x, y);
12391
12392       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12393         DrawLevelFieldCrumbledNeighbours(x, y);
12394
12395       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12396         DrawTwinkleOnField(x, y);
12397     }
12398
12399     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12400   }
12401 #endif
12402
12403   DrawAllPlayers();
12404   PlayAllPlayersSound();
12405
12406   for (i = 0; i < MAX_PLAYERS; i++)
12407   {
12408     struct PlayerInfo *player = &stored_player[i];
12409
12410     if (player->show_envelope != 0 && (!player->active ||
12411                                        player->MovPos == 0))
12412     {
12413       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12414
12415       player->show_envelope = 0;
12416     }
12417   }
12418
12419   // use random number generator in every frame to make it less predictable
12420   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12421     RND(1);
12422
12423   mouse_action_last = mouse_action;
12424 }
12425
12426 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12427 {
12428   int min_x = x, min_y = y, max_x = x, max_y = y;
12429   int i;
12430
12431   for (i = 0; i < MAX_PLAYERS; i++)
12432   {
12433     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12434
12435     if (!stored_player[i].active || &stored_player[i] == player)
12436       continue;
12437
12438     min_x = MIN(min_x, jx);
12439     min_y = MIN(min_y, jy);
12440     max_x = MAX(max_x, jx);
12441     max_y = MAX(max_y, jy);
12442   }
12443
12444   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12445 }
12446
12447 static boolean AllPlayersInVisibleScreen(void)
12448 {
12449   int i;
12450
12451   for (i = 0; i < MAX_PLAYERS; i++)
12452   {
12453     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12454
12455     if (!stored_player[i].active)
12456       continue;
12457
12458     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12459       return FALSE;
12460   }
12461
12462   return TRUE;
12463 }
12464
12465 void ScrollLevel(int dx, int dy)
12466 {
12467   int scroll_offset = 2 * TILEX_VAR;
12468   int x, y;
12469
12470   BlitBitmap(drawto_field, drawto_field,
12471              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12472              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12473              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12474              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12475              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12476              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12477
12478   if (dx != 0)
12479   {
12480     x = (dx == 1 ? BX1 : BX2);
12481     for (y = BY1; y <= BY2; y++)
12482       DrawScreenField(x, y);
12483   }
12484
12485   if (dy != 0)
12486   {
12487     y = (dy == 1 ? BY1 : BY2);
12488     for (x = BX1; x <= BX2; x++)
12489       DrawScreenField(x, y);
12490   }
12491
12492   redraw_mask |= REDRAW_FIELD;
12493 }
12494
12495 static boolean canFallDown(struct PlayerInfo *player)
12496 {
12497   int jx = player->jx, jy = player->jy;
12498
12499   return (IN_LEV_FIELD(jx, jy + 1) &&
12500           (IS_FREE(jx, jy + 1) ||
12501            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12502           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12503           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12504 }
12505
12506 static boolean canPassField(int x, int y, int move_dir)
12507 {
12508   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12509   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12510   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12511   int nextx = x + dx;
12512   int nexty = y + dy;
12513   int element = Tile[x][y];
12514
12515   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12516           !CAN_MOVE(element) &&
12517           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12518           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12519           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12520 }
12521
12522 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12523 {
12524   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12525   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12526   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12527   int newx = x + dx;
12528   int newy = y + dy;
12529
12530   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12531           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12532           (IS_DIGGABLE(Tile[newx][newy]) ||
12533            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12534            canPassField(newx, newy, move_dir)));
12535 }
12536
12537 static void CheckGravityMovement(struct PlayerInfo *player)
12538 {
12539   if (player->gravity && !player->programmed_action)
12540   {
12541     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12542     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12543     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12544     int jx = player->jx, jy = player->jy;
12545     boolean player_is_moving_to_valid_field =
12546       (!player_is_snapping &&
12547        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12548         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12549     boolean player_can_fall_down = canFallDown(player);
12550
12551     if (player_can_fall_down &&
12552         !player_is_moving_to_valid_field)
12553       player->programmed_action = MV_DOWN;
12554   }
12555 }
12556
12557 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12558 {
12559   return CheckGravityMovement(player);
12560
12561   if (player->gravity && !player->programmed_action)
12562   {
12563     int jx = player->jx, jy = player->jy;
12564     boolean field_under_player_is_free =
12565       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12566     boolean player_is_standing_on_valid_field =
12567       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12568        (IS_WALKABLE(Tile[jx][jy]) &&
12569         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12570
12571     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12572       player->programmed_action = MV_DOWN;
12573   }
12574 }
12575
12576 /*
12577   MovePlayerOneStep()
12578   -----------------------------------------------------------------------------
12579   dx, dy:               direction (non-diagonal) to try to move the player to
12580   real_dx, real_dy:     direction as read from input device (can be diagonal)
12581 */
12582
12583 boolean MovePlayerOneStep(struct PlayerInfo *player,
12584                           int dx, int dy, int real_dx, int real_dy)
12585 {
12586   int jx = player->jx, jy = player->jy;
12587   int new_jx = jx + dx, new_jy = jy + dy;
12588   int can_move;
12589   boolean player_can_move = !player->cannot_move;
12590
12591   if (!player->active || (!dx && !dy))
12592     return MP_NO_ACTION;
12593
12594   player->MovDir = (dx < 0 ? MV_LEFT :
12595                     dx > 0 ? MV_RIGHT :
12596                     dy < 0 ? MV_UP :
12597                     dy > 0 ? MV_DOWN :  MV_NONE);
12598
12599   if (!IN_LEV_FIELD(new_jx, new_jy))
12600     return MP_NO_ACTION;
12601
12602   if (!player_can_move)
12603   {
12604     if (player->MovPos == 0)
12605     {
12606       player->is_moving = FALSE;
12607       player->is_digging = FALSE;
12608       player->is_collecting = FALSE;
12609       player->is_snapping = FALSE;
12610       player->is_pushing = FALSE;
12611     }
12612   }
12613
12614   if (!network.enabled && game.centered_player_nr == -1 &&
12615       !AllPlayersInSight(player, new_jx, new_jy))
12616     return MP_NO_ACTION;
12617
12618   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12619   if (can_move != MP_MOVING)
12620     return can_move;
12621
12622   // check if DigField() has caused relocation of the player
12623   if (player->jx != jx || player->jy != jy)
12624     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12625
12626   StorePlayer[jx][jy] = 0;
12627   player->last_jx = jx;
12628   player->last_jy = jy;
12629   player->jx = new_jx;
12630   player->jy = new_jy;
12631   StorePlayer[new_jx][new_jy] = player->element_nr;
12632
12633   if (player->move_delay_value_next != -1)
12634   {
12635     player->move_delay_value = player->move_delay_value_next;
12636     player->move_delay_value_next = -1;
12637   }
12638
12639   player->MovPos =
12640     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12641
12642   player->step_counter++;
12643
12644   PlayerVisit[jx][jy] = FrameCounter;
12645
12646   player->is_moving = TRUE;
12647
12648 #if 1
12649   // should better be called in MovePlayer(), but this breaks some tapes
12650   ScrollPlayer(player, SCROLL_INIT);
12651 #endif
12652
12653   return MP_MOVING;
12654 }
12655
12656 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12657 {
12658   int jx = player->jx, jy = player->jy;
12659   int old_jx = jx, old_jy = jy;
12660   int moved = MP_NO_ACTION;
12661
12662   if (!player->active)
12663     return FALSE;
12664
12665   if (!dx && !dy)
12666   {
12667     if (player->MovPos == 0)
12668     {
12669       player->is_moving = FALSE;
12670       player->is_digging = FALSE;
12671       player->is_collecting = FALSE;
12672       player->is_snapping = FALSE;
12673       player->is_pushing = FALSE;
12674     }
12675
12676     return FALSE;
12677   }
12678
12679   if (player->move_delay > 0)
12680     return FALSE;
12681
12682   player->move_delay = -1;              // set to "uninitialized" value
12683
12684   // store if player is automatically moved to next field
12685   player->is_auto_moving = (player->programmed_action != MV_NONE);
12686
12687   // remove the last programmed player action
12688   player->programmed_action = 0;
12689
12690   if (player->MovPos)
12691   {
12692     // should only happen if pre-1.2 tape recordings are played
12693     // this is only for backward compatibility
12694
12695     int original_move_delay_value = player->move_delay_value;
12696
12697 #if DEBUG
12698     Debug("game:playing:MovePlayer",
12699           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12700           tape.counter);
12701 #endif
12702
12703     // scroll remaining steps with finest movement resolution
12704     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12705
12706     while (player->MovPos)
12707     {
12708       ScrollPlayer(player, SCROLL_GO_ON);
12709       ScrollScreen(NULL, SCROLL_GO_ON);
12710
12711       AdvanceFrameAndPlayerCounters(player->index_nr);
12712
12713       DrawAllPlayers();
12714       BackToFront_WithFrameDelay(0);
12715     }
12716
12717     player->move_delay_value = original_move_delay_value;
12718   }
12719
12720   player->is_active = FALSE;
12721
12722   if (player->last_move_dir & MV_HORIZONTAL)
12723   {
12724     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12725       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12726   }
12727   else
12728   {
12729     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12730       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12731   }
12732
12733   if (!moved && !player->is_active)
12734   {
12735     player->is_moving = FALSE;
12736     player->is_digging = FALSE;
12737     player->is_collecting = FALSE;
12738     player->is_snapping = FALSE;
12739     player->is_pushing = FALSE;
12740   }
12741
12742   jx = player->jx;
12743   jy = player->jy;
12744
12745   if (moved & MP_MOVING && !ScreenMovPos &&
12746       (player->index_nr == game.centered_player_nr ||
12747        game.centered_player_nr == -1))
12748   {
12749     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12750
12751     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12752     {
12753       // actual player has left the screen -- scroll in that direction
12754       if (jx != old_jx)         // player has moved horizontally
12755         scroll_x += (jx - old_jx);
12756       else                      // player has moved vertically
12757         scroll_y += (jy - old_jy);
12758     }
12759     else
12760     {
12761       int offset_raw = game.scroll_delay_value;
12762
12763       if (jx != old_jx)         // player has moved horizontally
12764       {
12765         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12766         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12767         int new_scroll_x = jx - MIDPOSX + offset_x;
12768
12769         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12770             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12771           scroll_x = new_scroll_x;
12772
12773         // don't scroll over playfield boundaries
12774         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12775
12776         // don't scroll more than one field at a time
12777         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12778
12779         // don't scroll against the player's moving direction
12780         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12781             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12782           scroll_x = old_scroll_x;
12783       }
12784       else                      // player has moved vertically
12785       {
12786         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12787         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12788         int new_scroll_y = jy - MIDPOSY + offset_y;
12789
12790         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12791             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12792           scroll_y = new_scroll_y;
12793
12794         // don't scroll over playfield boundaries
12795         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
12796
12797         // don't scroll more than one field at a time
12798         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12799
12800         // don't scroll against the player's moving direction
12801         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12802             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12803           scroll_y = old_scroll_y;
12804       }
12805     }
12806
12807     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12808     {
12809       if (!network.enabled && game.centered_player_nr == -1 &&
12810           !AllPlayersInVisibleScreen())
12811       {
12812         scroll_x = old_scroll_x;
12813         scroll_y = old_scroll_y;
12814       }
12815       else
12816       {
12817         ScrollScreen(player, SCROLL_INIT);
12818         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12819       }
12820     }
12821   }
12822
12823   player->StepFrame = 0;
12824
12825   if (moved & MP_MOVING)
12826   {
12827     if (old_jx != jx && old_jy == jy)
12828       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12829     else if (old_jx == jx && old_jy != jy)
12830       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12831
12832     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12833
12834     player->last_move_dir = player->MovDir;
12835     player->is_moving = TRUE;
12836     player->is_snapping = FALSE;
12837     player->is_switching = FALSE;
12838     player->is_dropping = FALSE;
12839     player->is_dropping_pressed = FALSE;
12840     player->drop_pressed_delay = 0;
12841
12842 #if 0
12843     // should better be called here than above, but this breaks some tapes
12844     ScrollPlayer(player, SCROLL_INIT);
12845 #endif
12846   }
12847   else
12848   {
12849     CheckGravityMovementWhenNotMoving(player);
12850
12851     player->is_moving = FALSE;
12852
12853     /* at this point, the player is allowed to move, but cannot move right now
12854        (e.g. because of something blocking the way) -- ensure that the player
12855        is also allowed to move in the next frame (in old versions before 3.1.1,
12856        the player was forced to wait again for eight frames before next try) */
12857
12858     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12859       player->move_delay = 0;   // allow direct movement in the next frame
12860   }
12861
12862   if (player->move_delay == -1)         // not yet initialized by DigField()
12863     player->move_delay = player->move_delay_value;
12864
12865   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12866   {
12867     TestIfPlayerTouchesBadThing(jx, jy);
12868     TestIfPlayerTouchesCustomElement(jx, jy);
12869   }
12870
12871   if (!player->active)
12872     RemovePlayer(player);
12873
12874   return moved;
12875 }
12876
12877 void ScrollPlayer(struct PlayerInfo *player, int mode)
12878 {
12879   int jx = player->jx, jy = player->jy;
12880   int last_jx = player->last_jx, last_jy = player->last_jy;
12881   int move_stepsize = TILEX / player->move_delay_value;
12882
12883   if (!player->active)
12884     return;
12885
12886   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
12887     return;
12888
12889   if (mode == SCROLL_INIT)
12890   {
12891     player->actual_frame_counter = FrameCounter;
12892     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12893
12894     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12895         Tile[last_jx][last_jy] == EL_EMPTY)
12896     {
12897       int last_field_block_delay = 0;   // start with no blocking at all
12898       int block_delay_adjustment = player->block_delay_adjustment;
12899
12900       // if player blocks last field, add delay for exactly one move
12901       if (player->block_last_field)
12902       {
12903         last_field_block_delay += player->move_delay_value;
12904
12905         // when blocking enabled, prevent moving up despite gravity
12906         if (player->gravity && player->MovDir == MV_UP)
12907           block_delay_adjustment = -1;
12908       }
12909
12910       // add block delay adjustment (also possible when not blocking)
12911       last_field_block_delay += block_delay_adjustment;
12912
12913       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12914       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12915     }
12916
12917     if (player->MovPos != 0)    // player has not yet reached destination
12918       return;
12919   }
12920   else if (!FrameReached(&player->actual_frame_counter, 1))
12921     return;
12922
12923   if (player->MovPos != 0)
12924   {
12925     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12926     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12927
12928     // before DrawPlayer() to draw correct player graphic for this case
12929     if (player->MovPos == 0)
12930       CheckGravityMovement(player);
12931   }
12932
12933   if (player->MovPos == 0)      // player reached destination field
12934   {
12935     if (player->move_delay_reset_counter > 0)
12936     {
12937       player->move_delay_reset_counter--;
12938
12939       if (player->move_delay_reset_counter == 0)
12940       {
12941         // continue with normal speed after quickly moving through gate
12942         HALVE_PLAYER_SPEED(player);
12943
12944         // be able to make the next move without delay
12945         player->move_delay = 0;
12946       }
12947     }
12948
12949     player->last_jx = jx;
12950     player->last_jy = jy;
12951
12952     if (Tile[jx][jy] == EL_EXIT_OPEN ||
12953         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
12954         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
12955         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
12956         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12957         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12958         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
12959         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
12960     {
12961       ExitPlayer(player);
12962
12963       if (game.players_still_needed == 0 &&
12964           (game.friends_still_needed == 0 ||
12965            IS_SP_ELEMENT(Tile[jx][jy])))
12966         LevelSolved();
12967     }
12968
12969     // this breaks one level: "machine", level 000
12970     {
12971       int move_direction = player->MovDir;
12972       int enter_side = MV_DIR_OPPOSITE(move_direction);
12973       int leave_side = move_direction;
12974       int old_jx = last_jx;
12975       int old_jy = last_jy;
12976       int old_element = Tile[old_jx][old_jy];
12977       int new_element = Tile[jx][jy];
12978
12979       if (IS_CUSTOM_ELEMENT(old_element))
12980         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12981                                    CE_LEFT_BY_PLAYER,
12982                                    player->index_bit, leave_side);
12983
12984       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12985                                           CE_PLAYER_LEAVES_X,
12986                                           player->index_bit, leave_side);
12987
12988       if (IS_CUSTOM_ELEMENT(new_element))
12989         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12990                                    player->index_bit, enter_side);
12991
12992       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12993                                           CE_PLAYER_ENTERS_X,
12994                                           player->index_bit, enter_side);
12995
12996       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12997                                         CE_MOVE_OF_X, move_direction);
12998     }
12999
13000     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13001     {
13002       TestIfPlayerTouchesBadThing(jx, jy);
13003       TestIfPlayerTouchesCustomElement(jx, jy);
13004
13005       /* needed because pushed element has not yet reached its destination,
13006          so it would trigger a change event at its previous field location */
13007       if (!player->is_pushing)
13008         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13009
13010       if (!player->active)
13011         RemovePlayer(player);
13012     }
13013
13014     if (!game.LevelSolved && level.use_step_counter)
13015     {
13016       int i;
13017
13018       TimePlayed++;
13019
13020       if (TimeLeft > 0)
13021       {
13022         TimeLeft--;
13023
13024         if (TimeLeft <= 10 && setup.time_limit)
13025           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13026
13027         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13028
13029         DisplayGameControlValues();
13030
13031         if (!TimeLeft && setup.time_limit)
13032           for (i = 0; i < MAX_PLAYERS; i++)
13033             KillPlayer(&stored_player[i]);
13034       }
13035       else if (game.no_time_limit && !game.all_players_gone)
13036       {
13037         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13038
13039         DisplayGameControlValues();
13040       }
13041     }
13042
13043     if (tape.single_step && tape.recording && !tape.pausing &&
13044         !player->programmed_action)
13045       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13046
13047     if (!player->programmed_action)
13048       CheckSaveEngineSnapshot(player);
13049   }
13050 }
13051
13052 void ScrollScreen(struct PlayerInfo *player, int mode)
13053 {
13054   static unsigned int screen_frame_counter = 0;
13055
13056   if (mode == SCROLL_INIT)
13057   {
13058     // set scrolling step size according to actual player's moving speed
13059     ScrollStepSize = TILEX / player->move_delay_value;
13060
13061     screen_frame_counter = FrameCounter;
13062     ScreenMovDir = player->MovDir;
13063     ScreenMovPos = player->MovPos;
13064     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13065     return;
13066   }
13067   else if (!FrameReached(&screen_frame_counter, 1))
13068     return;
13069
13070   if (ScreenMovPos)
13071   {
13072     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13073     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13074     redraw_mask |= REDRAW_FIELD;
13075   }
13076   else
13077     ScreenMovDir = MV_NONE;
13078 }
13079
13080 void TestIfPlayerTouchesCustomElement(int x, int y)
13081 {
13082   static int xy[4][2] =
13083   {
13084     { 0, -1 },
13085     { -1, 0 },
13086     { +1, 0 },
13087     { 0, +1 }
13088   };
13089   static int trigger_sides[4][2] =
13090   {
13091     // center side       border side
13092     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13093     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13094     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13095     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13096   };
13097   static int touch_dir[4] =
13098   {
13099     MV_LEFT | MV_RIGHT,
13100     MV_UP   | MV_DOWN,
13101     MV_UP   | MV_DOWN,
13102     MV_LEFT | MV_RIGHT
13103   };
13104   int center_element = Tile[x][y];      // should always be non-moving!
13105   int i;
13106
13107   for (i = 0; i < NUM_DIRECTIONS; i++)
13108   {
13109     int xx = x + xy[i][0];
13110     int yy = y + xy[i][1];
13111     int center_side = trigger_sides[i][0];
13112     int border_side = trigger_sides[i][1];
13113     int border_element;
13114
13115     if (!IN_LEV_FIELD(xx, yy))
13116       continue;
13117
13118     if (IS_PLAYER(x, y))                // player found at center element
13119     {
13120       struct PlayerInfo *player = PLAYERINFO(x, y);
13121
13122       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13123         border_element = Tile[xx][yy];          // may be moving!
13124       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13125         border_element = Tile[xx][yy];
13126       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13127         border_element = MovingOrBlocked2Element(xx, yy);
13128       else
13129         continue;               // center and border element do not touch
13130
13131       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13132                                  player->index_bit, border_side);
13133       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13134                                           CE_PLAYER_TOUCHES_X,
13135                                           player->index_bit, border_side);
13136
13137       {
13138         /* use player element that is initially defined in the level playfield,
13139            not the player element that corresponds to the runtime player number
13140            (example: a level that contains EL_PLAYER_3 as the only player would
13141            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13142         int player_element = PLAYERINFO(x, y)->initial_element;
13143
13144         CheckElementChangeBySide(xx, yy, border_element, player_element,
13145                                  CE_TOUCHING_X, border_side);
13146       }
13147     }
13148     else if (IS_PLAYER(xx, yy))         // player found at border element
13149     {
13150       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13151
13152       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13153       {
13154         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13155           continue;             // center and border element do not touch
13156       }
13157
13158       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13159                                  player->index_bit, center_side);
13160       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13161                                           CE_PLAYER_TOUCHES_X,
13162                                           player->index_bit, center_side);
13163
13164       {
13165         /* use player element that is initially defined in the level playfield,
13166            not the player element that corresponds to the runtime player number
13167            (example: a level that contains EL_PLAYER_3 as the only player would
13168            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13169         int player_element = PLAYERINFO(xx, yy)->initial_element;
13170
13171         CheckElementChangeBySide(x, y, center_element, player_element,
13172                                  CE_TOUCHING_X, center_side);
13173       }
13174
13175       break;
13176     }
13177   }
13178 }
13179
13180 void TestIfElementTouchesCustomElement(int x, int y)
13181 {
13182   static int xy[4][2] =
13183   {
13184     { 0, -1 },
13185     { -1, 0 },
13186     { +1, 0 },
13187     { 0, +1 }
13188   };
13189   static int trigger_sides[4][2] =
13190   {
13191     // center side      border side
13192     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13193     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13194     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13195     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13196   };
13197   static int touch_dir[4] =
13198   {
13199     MV_LEFT | MV_RIGHT,
13200     MV_UP   | MV_DOWN,
13201     MV_UP   | MV_DOWN,
13202     MV_LEFT | MV_RIGHT
13203   };
13204   boolean change_center_element = FALSE;
13205   int center_element = Tile[x][y];      // should always be non-moving!
13206   int border_element_old[NUM_DIRECTIONS];
13207   int i;
13208
13209   for (i = 0; i < NUM_DIRECTIONS; i++)
13210   {
13211     int xx = x + xy[i][0];
13212     int yy = y + xy[i][1];
13213     int border_element;
13214
13215     border_element_old[i] = -1;
13216
13217     if (!IN_LEV_FIELD(xx, yy))
13218       continue;
13219
13220     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13221       border_element = Tile[xx][yy];    // may be moving!
13222     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13223       border_element = Tile[xx][yy];
13224     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13225       border_element = MovingOrBlocked2Element(xx, yy);
13226     else
13227       continue;                 // center and border element do not touch
13228
13229     border_element_old[i] = border_element;
13230   }
13231
13232   for (i = 0; i < NUM_DIRECTIONS; i++)
13233   {
13234     int xx = x + xy[i][0];
13235     int yy = y + xy[i][1];
13236     int center_side = trigger_sides[i][0];
13237     int border_element = border_element_old[i];
13238
13239     if (border_element == -1)
13240       continue;
13241
13242     // check for change of border element
13243     CheckElementChangeBySide(xx, yy, border_element, center_element,
13244                              CE_TOUCHING_X, center_side);
13245
13246     // (center element cannot be player, so we dont have to check this here)
13247   }
13248
13249   for (i = 0; i < NUM_DIRECTIONS; i++)
13250   {
13251     int xx = x + xy[i][0];
13252     int yy = y + xy[i][1];
13253     int border_side = trigger_sides[i][1];
13254     int border_element = border_element_old[i];
13255
13256     if (border_element == -1)
13257       continue;
13258
13259     // check for change of center element (but change it only once)
13260     if (!change_center_element)
13261       change_center_element =
13262         CheckElementChangeBySide(x, y, center_element, border_element,
13263                                  CE_TOUCHING_X, border_side);
13264
13265     if (IS_PLAYER(xx, yy))
13266     {
13267       /* use player element that is initially defined in the level playfield,
13268          not the player element that corresponds to the runtime player number
13269          (example: a level that contains EL_PLAYER_3 as the only player would
13270          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13271       int player_element = PLAYERINFO(xx, yy)->initial_element;
13272
13273       CheckElementChangeBySide(x, y, center_element, player_element,
13274                                CE_TOUCHING_X, border_side);
13275     }
13276   }
13277 }
13278
13279 void TestIfElementHitsCustomElement(int x, int y, int direction)
13280 {
13281   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13282   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13283   int hitx = x + dx, hity = y + dy;
13284   int hitting_element = Tile[x][y];
13285   int touched_element;
13286
13287   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13288     return;
13289
13290   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13291                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13292
13293   if (IN_LEV_FIELD(hitx, hity))
13294   {
13295     int opposite_direction = MV_DIR_OPPOSITE(direction);
13296     int hitting_side = direction;
13297     int touched_side = opposite_direction;
13298     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13299                           MovDir[hitx][hity] != direction ||
13300                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13301
13302     object_hit = TRUE;
13303
13304     if (object_hit)
13305     {
13306       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13307                                CE_HITTING_X, touched_side);
13308
13309       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13310                                CE_HIT_BY_X, hitting_side);
13311
13312       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13313                                CE_HIT_BY_SOMETHING, opposite_direction);
13314
13315       if (IS_PLAYER(hitx, hity))
13316       {
13317         /* use player element that is initially defined in the level playfield,
13318            not the player element that corresponds to the runtime player number
13319            (example: a level that contains EL_PLAYER_3 as the only player would
13320            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13321         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13322
13323         CheckElementChangeBySide(x, y, hitting_element, player_element,
13324                                  CE_HITTING_X, touched_side);
13325       }
13326     }
13327   }
13328
13329   // "hitting something" is also true when hitting the playfield border
13330   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13331                            CE_HITTING_SOMETHING, direction);
13332 }
13333
13334 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13335 {
13336   int i, kill_x = -1, kill_y = -1;
13337
13338   int bad_element = -1;
13339   static int test_xy[4][2] =
13340   {
13341     { 0, -1 },
13342     { -1, 0 },
13343     { +1, 0 },
13344     { 0, +1 }
13345   };
13346   static int test_dir[4] =
13347   {
13348     MV_UP,
13349     MV_LEFT,
13350     MV_RIGHT,
13351     MV_DOWN
13352   };
13353
13354   for (i = 0; i < NUM_DIRECTIONS; i++)
13355   {
13356     int test_x, test_y, test_move_dir, test_element;
13357
13358     test_x = good_x + test_xy[i][0];
13359     test_y = good_y + test_xy[i][1];
13360
13361     if (!IN_LEV_FIELD(test_x, test_y))
13362       continue;
13363
13364     test_move_dir =
13365       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13366
13367     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13368
13369     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13370        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13371     */
13372     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13373         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13374     {
13375       kill_x = test_x;
13376       kill_y = test_y;
13377       bad_element = test_element;
13378
13379       break;
13380     }
13381   }
13382
13383   if (kill_x != -1 || kill_y != -1)
13384   {
13385     if (IS_PLAYER(good_x, good_y))
13386     {
13387       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13388
13389       if (player->shield_deadly_time_left > 0 &&
13390           !IS_INDESTRUCTIBLE(bad_element))
13391         Bang(kill_x, kill_y);
13392       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13393         KillPlayer(player);
13394     }
13395     else
13396       Bang(good_x, good_y);
13397   }
13398 }
13399
13400 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13401 {
13402   int i, kill_x = -1, kill_y = -1;
13403   int bad_element = Tile[bad_x][bad_y];
13404   static int test_xy[4][2] =
13405   {
13406     { 0, -1 },
13407     { -1, 0 },
13408     { +1, 0 },
13409     { 0, +1 }
13410   };
13411   static int touch_dir[4] =
13412   {
13413     MV_LEFT | MV_RIGHT,
13414     MV_UP   | MV_DOWN,
13415     MV_UP   | MV_DOWN,
13416     MV_LEFT | MV_RIGHT
13417   };
13418   static int test_dir[4] =
13419   {
13420     MV_UP,
13421     MV_LEFT,
13422     MV_RIGHT,
13423     MV_DOWN
13424   };
13425
13426   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13427     return;
13428
13429   for (i = 0; i < NUM_DIRECTIONS; i++)
13430   {
13431     int test_x, test_y, test_move_dir, test_element;
13432
13433     test_x = bad_x + test_xy[i][0];
13434     test_y = bad_y + test_xy[i][1];
13435
13436     if (!IN_LEV_FIELD(test_x, test_y))
13437       continue;
13438
13439     test_move_dir =
13440       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13441
13442     test_element = Tile[test_x][test_y];
13443
13444     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13445        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13446     */
13447     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13448         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13449     {
13450       // good thing is player or penguin that does not move away
13451       if (IS_PLAYER(test_x, test_y))
13452       {
13453         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13454
13455         if (bad_element == EL_ROBOT && player->is_moving)
13456           continue;     // robot does not kill player if he is moving
13457
13458         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13459         {
13460           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13461             continue;           // center and border element do not touch
13462         }
13463
13464         kill_x = test_x;
13465         kill_y = test_y;
13466
13467         break;
13468       }
13469       else if (test_element == EL_PENGUIN)
13470       {
13471         kill_x = test_x;
13472         kill_y = test_y;
13473
13474         break;
13475       }
13476     }
13477   }
13478
13479   if (kill_x != -1 || kill_y != -1)
13480   {
13481     if (IS_PLAYER(kill_x, kill_y))
13482     {
13483       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13484
13485       if (player->shield_deadly_time_left > 0 &&
13486           !IS_INDESTRUCTIBLE(bad_element))
13487         Bang(bad_x, bad_y);
13488       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13489         KillPlayer(player);
13490     }
13491     else
13492       Bang(kill_x, kill_y);
13493   }
13494 }
13495
13496 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13497 {
13498   int bad_element = Tile[bad_x][bad_y];
13499   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13500   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13501   int test_x = bad_x + dx, test_y = bad_y + dy;
13502   int test_move_dir, test_element;
13503   int kill_x = -1, kill_y = -1;
13504
13505   if (!IN_LEV_FIELD(test_x, test_y))
13506     return;
13507
13508   test_move_dir =
13509     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13510
13511   test_element = Tile[test_x][test_y];
13512
13513   if (test_move_dir != bad_move_dir)
13514   {
13515     // good thing can be 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       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13521          player as being hit when he is moving towards the bad thing, because
13522          the "get hit by" condition would be lost after the player stops) */
13523       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13524         return;         // player moves away from bad thing
13525
13526       kill_x = test_x;
13527       kill_y = test_y;
13528     }
13529     else if (test_element == EL_PENGUIN)
13530     {
13531       kill_x = test_x;
13532       kill_y = test_y;
13533     }
13534   }
13535
13536   if (kill_x != -1 || kill_y != -1)
13537   {
13538     if (IS_PLAYER(kill_x, kill_y))
13539     {
13540       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13541
13542       if (player->shield_deadly_time_left > 0 &&
13543           !IS_INDESTRUCTIBLE(bad_element))
13544         Bang(bad_x, bad_y);
13545       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13546         KillPlayer(player);
13547     }
13548     else
13549       Bang(kill_x, kill_y);
13550   }
13551 }
13552
13553 void TestIfPlayerTouchesBadThing(int x, int y)
13554 {
13555   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13556 }
13557
13558 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13559 {
13560   TestIfGoodThingHitsBadThing(x, y, move_dir);
13561 }
13562
13563 void TestIfBadThingTouchesPlayer(int x, int y)
13564 {
13565   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13566 }
13567
13568 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13569 {
13570   TestIfBadThingHitsGoodThing(x, y, move_dir);
13571 }
13572
13573 void TestIfFriendTouchesBadThing(int x, int y)
13574 {
13575   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13576 }
13577
13578 void TestIfBadThingTouchesFriend(int x, int y)
13579 {
13580   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13581 }
13582
13583 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13584 {
13585   int i, kill_x = bad_x, kill_y = bad_y;
13586   static int xy[4][2] =
13587   {
13588     { 0, -1 },
13589     { -1, 0 },
13590     { +1, 0 },
13591     { 0, +1 }
13592   };
13593
13594   for (i = 0; i < NUM_DIRECTIONS; i++)
13595   {
13596     int x, y, element;
13597
13598     x = bad_x + xy[i][0];
13599     y = bad_y + xy[i][1];
13600     if (!IN_LEV_FIELD(x, y))
13601       continue;
13602
13603     element = Tile[x][y];
13604     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13605         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13606     {
13607       kill_x = x;
13608       kill_y = y;
13609       break;
13610     }
13611   }
13612
13613   if (kill_x != bad_x || kill_y != bad_y)
13614     Bang(bad_x, bad_y);
13615 }
13616
13617 void KillPlayer(struct PlayerInfo *player)
13618 {
13619   int jx = player->jx, jy = player->jy;
13620
13621   if (!player->active)
13622     return;
13623
13624 #if 0
13625   Debug("game:playing:KillPlayer",
13626         "0: killed == %d, active == %d, reanimated == %d",
13627         player->killed, player->active, player->reanimated);
13628 #endif
13629
13630   /* the following code was introduced to prevent an infinite loop when calling
13631      -> Bang()
13632      -> CheckTriggeredElementChangeExt()
13633      -> ExecuteCustomElementAction()
13634      -> KillPlayer()
13635      -> (infinitely repeating the above sequence of function calls)
13636      which occurs when killing the player while having a CE with the setting
13637      "kill player X when explosion of <player X>"; the solution using a new
13638      field "player->killed" was chosen for backwards compatibility, although
13639      clever use of the fields "player->active" etc. would probably also work */
13640 #if 1
13641   if (player->killed)
13642     return;
13643 #endif
13644
13645   player->killed = TRUE;
13646
13647   // remove accessible field at the player's position
13648   Tile[jx][jy] = EL_EMPTY;
13649
13650   // deactivate shield (else Bang()/Explode() would not work right)
13651   player->shield_normal_time_left = 0;
13652   player->shield_deadly_time_left = 0;
13653
13654 #if 0
13655   Debug("game:playing:KillPlayer",
13656         "1: killed == %d, active == %d, reanimated == %d",
13657         player->killed, player->active, player->reanimated);
13658 #endif
13659
13660   Bang(jx, jy);
13661
13662 #if 0
13663   Debug("game:playing:KillPlayer",
13664         "2: killed == %d, active == %d, reanimated == %d",
13665         player->killed, player->active, player->reanimated);
13666 #endif
13667
13668   if (player->reanimated)       // killed player may have been reanimated
13669     player->killed = player->reanimated = FALSE;
13670   else
13671     BuryPlayer(player);
13672 }
13673
13674 static void KillPlayerUnlessEnemyProtected(int x, int y)
13675 {
13676   if (!PLAYER_ENEMY_PROTECTED(x, y))
13677     KillPlayer(PLAYERINFO(x, y));
13678 }
13679
13680 static void KillPlayerUnlessExplosionProtected(int x, int y)
13681 {
13682   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13683     KillPlayer(PLAYERINFO(x, y));
13684 }
13685
13686 void BuryPlayer(struct PlayerInfo *player)
13687 {
13688   int jx = player->jx, jy = player->jy;
13689
13690   if (!player->active)
13691     return;
13692
13693   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13694   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13695
13696   RemovePlayer(player);
13697
13698   player->buried = TRUE;
13699
13700   if (game.all_players_gone)
13701     game.GameOver = TRUE;
13702 }
13703
13704 void RemovePlayer(struct PlayerInfo *player)
13705 {
13706   int jx = player->jx, jy = player->jy;
13707   int i, found = FALSE;
13708
13709   player->present = FALSE;
13710   player->active = FALSE;
13711
13712   // required for some CE actions (even if the player is not active anymore)
13713   player->MovPos = 0;
13714
13715   if (!ExplodeField[jx][jy])
13716     StorePlayer[jx][jy] = 0;
13717
13718   if (player->is_moving)
13719     TEST_DrawLevelField(player->last_jx, player->last_jy);
13720
13721   for (i = 0; i < MAX_PLAYERS; i++)
13722     if (stored_player[i].active)
13723       found = TRUE;
13724
13725   if (!found)
13726   {
13727     game.all_players_gone = TRUE;
13728     game.GameOver = TRUE;
13729   }
13730
13731   game.exit_x = game.robot_wheel_x = jx;
13732   game.exit_y = game.robot_wheel_y = jy;
13733 }
13734
13735 void ExitPlayer(struct PlayerInfo *player)
13736 {
13737   DrawPlayer(player);   // needed here only to cleanup last field
13738   RemovePlayer(player);
13739
13740   if (game.players_still_needed > 0)
13741     game.players_still_needed--;
13742 }
13743
13744 static void setFieldForSnapping(int x, int y, int element, int direction)
13745 {
13746   struct ElementInfo *ei = &element_info[element];
13747   int direction_bit = MV_DIR_TO_BIT(direction);
13748   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13749   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13750                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13751
13752   Tile[x][y] = EL_ELEMENT_SNAPPING;
13753   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13754
13755   ResetGfxAnimation(x, y);
13756
13757   GfxElement[x][y] = element;
13758   GfxAction[x][y] = action;
13759   GfxDir[x][y] = direction;
13760   GfxFrame[x][y] = -1;
13761 }
13762
13763 /*
13764   =============================================================================
13765   checkDiagonalPushing()
13766   -----------------------------------------------------------------------------
13767   check if diagonal input device direction results in pushing of object
13768   (by checking if the alternative direction is walkable, diggable, ...)
13769   =============================================================================
13770 */
13771
13772 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13773                                     int x, int y, int real_dx, int real_dy)
13774 {
13775   int jx, jy, dx, dy, xx, yy;
13776
13777   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13778     return TRUE;
13779
13780   // diagonal direction: check alternative direction
13781   jx = player->jx;
13782   jy = player->jy;
13783   dx = x - jx;
13784   dy = y - jy;
13785   xx = jx + (dx == 0 ? real_dx : 0);
13786   yy = jy + (dy == 0 ? real_dy : 0);
13787
13788   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
13789 }
13790
13791 /*
13792   =============================================================================
13793   DigField()
13794   -----------------------------------------------------------------------------
13795   x, y:                 field next to player (non-diagonal) to try to dig to
13796   real_dx, real_dy:     direction as read from input device (can be diagonal)
13797   =============================================================================
13798 */
13799
13800 static int DigField(struct PlayerInfo *player,
13801                     int oldx, int oldy, int x, int y,
13802                     int real_dx, int real_dy, int mode)
13803 {
13804   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13805   boolean player_was_pushing = player->is_pushing;
13806   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13807   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13808   int jx = oldx, jy = oldy;
13809   int dx = x - jx, dy = y - jy;
13810   int nextx = x + dx, nexty = y + dy;
13811   int move_direction = (dx == -1 ? MV_LEFT  :
13812                         dx == +1 ? MV_RIGHT :
13813                         dy == -1 ? MV_UP    :
13814                         dy == +1 ? MV_DOWN  : MV_NONE);
13815   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13816   int dig_side = MV_DIR_OPPOSITE(move_direction);
13817   int old_element = Tile[jx][jy];
13818   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13819   int collect_count;
13820
13821   if (is_player)                // function can also be called by EL_PENGUIN
13822   {
13823     if (player->MovPos == 0)
13824     {
13825       player->is_digging = FALSE;
13826       player->is_collecting = FALSE;
13827     }
13828
13829     if (player->MovPos == 0)    // last pushing move finished
13830       player->is_pushing = FALSE;
13831
13832     if (mode == DF_NO_PUSH)     // player just stopped pushing
13833     {
13834       player->is_switching = FALSE;
13835       player->push_delay = -1;
13836
13837       return MP_NO_ACTION;
13838     }
13839   }
13840
13841   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13842     old_element = Back[jx][jy];
13843
13844   // in case of element dropped at player position, check background
13845   else if (Back[jx][jy] != EL_EMPTY &&
13846            game.engine_version >= VERSION_IDENT(2,2,0,0))
13847     old_element = Back[jx][jy];
13848
13849   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13850     return MP_NO_ACTION;        // field has no opening in this direction
13851
13852   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13853     return MP_NO_ACTION;        // field has no opening in this direction
13854
13855   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13856   {
13857     SplashAcid(x, y);
13858
13859     Tile[jx][jy] = player->artwork_element;
13860     InitMovingField(jx, jy, MV_DOWN);
13861     Store[jx][jy] = EL_ACID;
13862     ContinueMoving(jx, jy);
13863     BuryPlayer(player);
13864
13865     return MP_DONT_RUN_INTO;
13866   }
13867
13868   if (player_can_move && DONT_RUN_INTO(element))
13869   {
13870     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13871
13872     return MP_DONT_RUN_INTO;
13873   }
13874
13875   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13876     return MP_NO_ACTION;
13877
13878   collect_count = element_info[element].collect_count_initial;
13879
13880   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
13881     return MP_NO_ACTION;
13882
13883   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13884     player_can_move = player_can_move_or_snap;
13885
13886   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13887       game.engine_version >= VERSION_IDENT(2,2,0,0))
13888   {
13889     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13890                                player->index_bit, dig_side);
13891     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13892                                         player->index_bit, dig_side);
13893
13894     if (element == EL_DC_LANDMINE)
13895       Bang(x, y);
13896
13897     if (Tile[x][y] != element)          // field changed by snapping
13898       return MP_ACTION;
13899
13900     return MP_NO_ACTION;
13901   }
13902
13903   if (player->gravity && is_player && !player->is_auto_moving &&
13904       canFallDown(player) && move_direction != MV_DOWN &&
13905       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13906     return MP_NO_ACTION;        // player cannot walk here due to gravity
13907
13908   if (player_can_move &&
13909       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13910   {
13911     int sound_element = SND_ELEMENT(element);
13912     int sound_action = ACTION_WALKING;
13913
13914     if (IS_RND_GATE(element))
13915     {
13916       if (!player->key[RND_GATE_NR(element)])
13917         return MP_NO_ACTION;
13918     }
13919     else if (IS_RND_GATE_GRAY(element))
13920     {
13921       if (!player->key[RND_GATE_GRAY_NR(element)])
13922         return MP_NO_ACTION;
13923     }
13924     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13925     {
13926       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13927         return MP_NO_ACTION;
13928     }
13929     else if (element == EL_EXIT_OPEN ||
13930              element == EL_EM_EXIT_OPEN ||
13931              element == EL_EM_EXIT_OPENING ||
13932              element == EL_STEEL_EXIT_OPEN ||
13933              element == EL_EM_STEEL_EXIT_OPEN ||
13934              element == EL_EM_STEEL_EXIT_OPENING ||
13935              element == EL_SP_EXIT_OPEN ||
13936              element == EL_SP_EXIT_OPENING)
13937     {
13938       sound_action = ACTION_PASSING;    // player is passing exit
13939     }
13940     else if (element == EL_EMPTY)
13941     {
13942       sound_action = ACTION_MOVING;             // nothing to walk on
13943     }
13944
13945     // play sound from background or player, whatever is available
13946     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13947       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13948     else
13949       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13950   }
13951   else if (player_can_move &&
13952            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13953   {
13954     if (!ACCESS_FROM(element, opposite_direction))
13955       return MP_NO_ACTION;      // field not accessible from this direction
13956
13957     if (CAN_MOVE(element))      // only fixed elements can be passed!
13958       return MP_NO_ACTION;
13959
13960     if (IS_EM_GATE(element))
13961     {
13962       if (!player->key[EM_GATE_NR(element)])
13963         return MP_NO_ACTION;
13964     }
13965     else if (IS_EM_GATE_GRAY(element))
13966     {
13967       if (!player->key[EM_GATE_GRAY_NR(element)])
13968         return MP_NO_ACTION;
13969     }
13970     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13971     {
13972       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13973         return MP_NO_ACTION;
13974     }
13975     else if (IS_EMC_GATE(element))
13976     {
13977       if (!player->key[EMC_GATE_NR(element)])
13978         return MP_NO_ACTION;
13979     }
13980     else if (IS_EMC_GATE_GRAY(element))
13981     {
13982       if (!player->key[EMC_GATE_GRAY_NR(element)])
13983         return MP_NO_ACTION;
13984     }
13985     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13986     {
13987       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13988         return MP_NO_ACTION;
13989     }
13990     else if (element == EL_DC_GATE_WHITE ||
13991              element == EL_DC_GATE_WHITE_GRAY ||
13992              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13993     {
13994       if (player->num_white_keys == 0)
13995         return MP_NO_ACTION;
13996
13997       player->num_white_keys--;
13998     }
13999     else if (IS_SP_PORT(element))
14000     {
14001       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14002           element == EL_SP_GRAVITY_PORT_RIGHT ||
14003           element == EL_SP_GRAVITY_PORT_UP ||
14004           element == EL_SP_GRAVITY_PORT_DOWN)
14005         player->gravity = !player->gravity;
14006       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14007                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14008                element == EL_SP_GRAVITY_ON_PORT_UP ||
14009                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14010         player->gravity = TRUE;
14011       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14012                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14013                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14014                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14015         player->gravity = FALSE;
14016     }
14017
14018     // automatically move to the next field with double speed
14019     player->programmed_action = move_direction;
14020
14021     if (player->move_delay_reset_counter == 0)
14022     {
14023       player->move_delay_reset_counter = 2;     // two double speed steps
14024
14025       DOUBLE_PLAYER_SPEED(player);
14026     }
14027
14028     PlayLevelSoundAction(x, y, ACTION_PASSING);
14029   }
14030   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14031   {
14032     RemoveField(x, y);
14033
14034     if (mode != DF_SNAP)
14035     {
14036       GfxElement[x][y] = GFX_ELEMENT(element);
14037       player->is_digging = TRUE;
14038     }
14039
14040     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14041
14042     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14043                                         player->index_bit, dig_side);
14044
14045     // if digging triggered player relocation, finish digging tile
14046     if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14047       setFieldForSnapping(x, y, element, move_direction);
14048
14049     if (mode == DF_SNAP)
14050     {
14051       if (level.block_snap_field)
14052         setFieldForSnapping(x, y, element, move_direction);
14053       else
14054         TestIfElementTouchesCustomElement(x, y);        // for empty space
14055
14056       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14057                                           player->index_bit, dig_side);
14058     }
14059   }
14060   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14061   {
14062     RemoveField(x, y);
14063
14064     if (is_player && mode != DF_SNAP)
14065     {
14066       GfxElement[x][y] = element;
14067       player->is_collecting = TRUE;
14068     }
14069
14070     if (element == EL_SPEED_PILL)
14071     {
14072       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14073     }
14074     else if (element == EL_EXTRA_TIME && level.time > 0)
14075     {
14076       TimeLeft += level.extra_time;
14077
14078       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14079
14080       DisplayGameControlValues();
14081     }
14082     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14083     {
14084       player->shield_normal_time_left += level.shield_normal_time;
14085       if (element == EL_SHIELD_DEADLY)
14086         player->shield_deadly_time_left += level.shield_deadly_time;
14087     }
14088     else if (element == EL_DYNAMITE ||
14089              element == EL_EM_DYNAMITE ||
14090              element == EL_SP_DISK_RED)
14091     {
14092       if (player->inventory_size < MAX_INVENTORY_SIZE)
14093         player->inventory_element[player->inventory_size++] = element;
14094
14095       DrawGameDoorValues();
14096     }
14097     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14098     {
14099       player->dynabomb_count++;
14100       player->dynabombs_left++;
14101     }
14102     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14103     {
14104       player->dynabomb_size++;
14105     }
14106     else if (element == EL_DYNABOMB_INCREASE_POWER)
14107     {
14108       player->dynabomb_xl = TRUE;
14109     }
14110     else if (IS_KEY(element))
14111     {
14112       player->key[KEY_NR(element)] = TRUE;
14113
14114       DrawGameDoorValues();
14115     }
14116     else if (element == EL_DC_KEY_WHITE)
14117     {
14118       player->num_white_keys++;
14119
14120       // display white keys?
14121       // DrawGameDoorValues();
14122     }
14123     else if (IS_ENVELOPE(element))
14124     {
14125       player->show_envelope = element;
14126     }
14127     else if (element == EL_EMC_LENSES)
14128     {
14129       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14130
14131       RedrawAllInvisibleElementsForLenses();
14132     }
14133     else if (element == EL_EMC_MAGNIFIER)
14134     {
14135       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14136
14137       RedrawAllInvisibleElementsForMagnifier();
14138     }
14139     else if (IS_DROPPABLE(element) ||
14140              IS_THROWABLE(element))     // can be collected and dropped
14141     {
14142       int i;
14143
14144       if (collect_count == 0)
14145         player->inventory_infinite_element = element;
14146       else
14147         for (i = 0; i < collect_count; i++)
14148           if (player->inventory_size < MAX_INVENTORY_SIZE)
14149             player->inventory_element[player->inventory_size++] = element;
14150
14151       DrawGameDoorValues();
14152     }
14153     else if (collect_count > 0)
14154     {
14155       game.gems_still_needed -= collect_count;
14156       if (game.gems_still_needed < 0)
14157         game.gems_still_needed = 0;
14158
14159       game.snapshot.collected_item = TRUE;
14160
14161       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14162
14163       DisplayGameControlValues();
14164     }
14165
14166     RaiseScoreElement(element);
14167     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14168
14169     if (is_player)
14170     {
14171       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14172                                           player->index_bit, dig_side);
14173
14174       // if collecting triggered player relocation, finish collecting tile
14175       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14176         setFieldForSnapping(x, y, element, move_direction);
14177     }
14178
14179     if (mode == DF_SNAP)
14180     {
14181       if (level.block_snap_field)
14182         setFieldForSnapping(x, y, element, move_direction);
14183       else
14184         TestIfElementTouchesCustomElement(x, y);        // for empty space
14185
14186       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14187                                           player->index_bit, dig_side);
14188     }
14189   }
14190   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14191   {
14192     if (mode == DF_SNAP && element != EL_BD_ROCK)
14193       return MP_NO_ACTION;
14194
14195     if (CAN_FALL(element) && dy)
14196       return MP_NO_ACTION;
14197
14198     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14199         !(element == EL_SPRING && level.use_spring_bug))
14200       return MP_NO_ACTION;
14201
14202     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14203         ((move_direction & MV_VERTICAL &&
14204           ((element_info[element].move_pattern & MV_LEFT &&
14205             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14206            (element_info[element].move_pattern & MV_RIGHT &&
14207             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14208          (move_direction & MV_HORIZONTAL &&
14209           ((element_info[element].move_pattern & MV_UP &&
14210             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14211            (element_info[element].move_pattern & MV_DOWN &&
14212             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14213       return MP_NO_ACTION;
14214
14215     // do not push elements already moving away faster than player
14216     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14217         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14218       return MP_NO_ACTION;
14219
14220     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14221     {
14222       if (player->push_delay_value == -1 || !player_was_pushing)
14223         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14224     }
14225     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14226     {
14227       if (player->push_delay_value == -1)
14228         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14229     }
14230     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14231     {
14232       if (!player->is_pushing)
14233         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14234     }
14235
14236     player->is_pushing = TRUE;
14237     player->is_active = TRUE;
14238
14239     if (!(IN_LEV_FIELD(nextx, nexty) &&
14240           (IS_FREE(nextx, nexty) ||
14241            (IS_SB_ELEMENT(element) &&
14242             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14243            (IS_CUSTOM_ELEMENT(element) &&
14244             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14245       return MP_NO_ACTION;
14246
14247     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14248       return MP_NO_ACTION;
14249
14250     if (player->push_delay == -1)       // new pushing; restart delay
14251       player->push_delay = 0;
14252
14253     if (player->push_delay < player->push_delay_value &&
14254         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14255         element != EL_SPRING && element != EL_BALLOON)
14256     {
14257       // make sure that there is no move delay before next try to push
14258       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14259         player->move_delay = 0;
14260
14261       return MP_NO_ACTION;
14262     }
14263
14264     if (IS_CUSTOM_ELEMENT(element) &&
14265         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14266     {
14267       if (!DigFieldByCE(nextx, nexty, element))
14268         return MP_NO_ACTION;
14269     }
14270
14271     if (IS_SB_ELEMENT(element))
14272     {
14273       boolean sokoban_task_solved = FALSE;
14274
14275       if (element == EL_SOKOBAN_FIELD_FULL)
14276       {
14277         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14278
14279         IncrementSokobanFieldsNeeded();
14280         IncrementSokobanObjectsNeeded();
14281       }
14282
14283       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14284       {
14285         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14286
14287         DecrementSokobanFieldsNeeded();
14288         DecrementSokobanObjectsNeeded();
14289
14290         // sokoban object was pushed from empty field to sokoban field
14291         if (Back[x][y] == EL_EMPTY)
14292           sokoban_task_solved = TRUE;
14293       }
14294
14295       Tile[x][y] = EL_SOKOBAN_OBJECT;
14296
14297       if (Back[x][y] == Back[nextx][nexty])
14298         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14299       else if (Back[x][y] != 0)
14300         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14301                                     ACTION_EMPTYING);
14302       else
14303         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14304                                     ACTION_FILLING);
14305
14306       if (sokoban_task_solved &&
14307           game.sokoban_fields_still_needed == 0 &&
14308           game.sokoban_objects_still_needed == 0 &&
14309           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14310       {
14311         game.players_still_needed = 0;
14312
14313         LevelSolved();
14314
14315         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14316       }
14317     }
14318     else
14319       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14320
14321     InitMovingField(x, y, move_direction);
14322     GfxAction[x][y] = ACTION_PUSHING;
14323
14324     if (mode == DF_SNAP)
14325       ContinueMoving(x, y);
14326     else
14327       MovPos[x][y] = (dx != 0 ? dx : dy);
14328
14329     Pushed[x][y] = TRUE;
14330     Pushed[nextx][nexty] = TRUE;
14331
14332     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14333       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14334     else
14335       player->push_delay_value = -1;    // get new value later
14336
14337     // check for element change _after_ element has been pushed
14338     if (game.use_change_when_pushing_bug)
14339     {
14340       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14341                                  player->index_bit, dig_side);
14342       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14343                                           player->index_bit, dig_side);
14344     }
14345   }
14346   else if (IS_SWITCHABLE(element))
14347   {
14348     if (PLAYER_SWITCHING(player, x, y))
14349     {
14350       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14351                                           player->index_bit, dig_side);
14352
14353       return MP_ACTION;
14354     }
14355
14356     player->is_switching = TRUE;
14357     player->switch_x = x;
14358     player->switch_y = y;
14359
14360     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14361
14362     if (element == EL_ROBOT_WHEEL)
14363     {
14364       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14365
14366       game.robot_wheel_x = x;
14367       game.robot_wheel_y = y;
14368       game.robot_wheel_active = TRUE;
14369
14370       TEST_DrawLevelField(x, y);
14371     }
14372     else if (element == EL_SP_TERMINAL)
14373     {
14374       int xx, yy;
14375
14376       SCAN_PLAYFIELD(xx, yy)
14377       {
14378         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14379         {
14380           Bang(xx, yy);
14381         }
14382         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14383         {
14384           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14385
14386           ResetGfxAnimation(xx, yy);
14387           TEST_DrawLevelField(xx, yy);
14388         }
14389       }
14390     }
14391     else if (IS_BELT_SWITCH(element))
14392     {
14393       ToggleBeltSwitch(x, y);
14394     }
14395     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14396              element == EL_SWITCHGATE_SWITCH_DOWN ||
14397              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14398              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14399     {
14400       ToggleSwitchgateSwitch(x, y);
14401     }
14402     else if (element == EL_LIGHT_SWITCH ||
14403              element == EL_LIGHT_SWITCH_ACTIVE)
14404     {
14405       ToggleLightSwitch(x, y);
14406     }
14407     else if (element == EL_TIMEGATE_SWITCH ||
14408              element == EL_DC_TIMEGATE_SWITCH)
14409     {
14410       ActivateTimegateSwitch(x, y);
14411     }
14412     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14413              element == EL_BALLOON_SWITCH_RIGHT ||
14414              element == EL_BALLOON_SWITCH_UP    ||
14415              element == EL_BALLOON_SWITCH_DOWN  ||
14416              element == EL_BALLOON_SWITCH_NONE  ||
14417              element == EL_BALLOON_SWITCH_ANY)
14418     {
14419       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14420                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14421                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14422                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14423                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14424                              move_direction);
14425     }
14426     else if (element == EL_LAMP)
14427     {
14428       Tile[x][y] = EL_LAMP_ACTIVE;
14429       game.lights_still_needed--;
14430
14431       ResetGfxAnimation(x, y);
14432       TEST_DrawLevelField(x, y);
14433     }
14434     else if (element == EL_TIME_ORB_FULL)
14435     {
14436       Tile[x][y] = EL_TIME_ORB_EMPTY;
14437
14438       if (level.time > 0 || level.use_time_orb_bug)
14439       {
14440         TimeLeft += level.time_orb_time;
14441         game.no_time_limit = FALSE;
14442
14443         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14444
14445         DisplayGameControlValues();
14446       }
14447
14448       ResetGfxAnimation(x, y);
14449       TEST_DrawLevelField(x, y);
14450     }
14451     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14452              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14453     {
14454       int xx, yy;
14455
14456       game.ball_active = !game.ball_active;
14457
14458       SCAN_PLAYFIELD(xx, yy)
14459       {
14460         int e = Tile[xx][yy];
14461
14462         if (game.ball_active)
14463         {
14464           if (e == EL_EMC_MAGIC_BALL)
14465             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14466           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14467             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14468         }
14469         else
14470         {
14471           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14472             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14473           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14474             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14475         }
14476       }
14477     }
14478
14479     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14480                                         player->index_bit, dig_side);
14481
14482     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14483                                         player->index_bit, dig_side);
14484
14485     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14486                                         player->index_bit, dig_side);
14487
14488     return MP_ACTION;
14489   }
14490   else
14491   {
14492     if (!PLAYER_SWITCHING(player, x, y))
14493     {
14494       player->is_switching = TRUE;
14495       player->switch_x = x;
14496       player->switch_y = y;
14497
14498       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14499                                  player->index_bit, dig_side);
14500       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14501                                           player->index_bit, dig_side);
14502
14503       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14504                                  player->index_bit, dig_side);
14505       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14506                                           player->index_bit, dig_side);
14507     }
14508
14509     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14510                                player->index_bit, dig_side);
14511     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14512                                         player->index_bit, dig_side);
14513
14514     return MP_NO_ACTION;
14515   }
14516
14517   player->push_delay = -1;
14518
14519   if (is_player)                // function can also be called by EL_PENGUIN
14520   {
14521     if (Tile[x][y] != element)          // really digged/collected something
14522     {
14523       player->is_collecting = !player->is_digging;
14524       player->is_active = TRUE;
14525     }
14526   }
14527
14528   return MP_MOVING;
14529 }
14530
14531 static boolean DigFieldByCE(int x, int y, int digging_element)
14532 {
14533   int element = Tile[x][y];
14534
14535   if (!IS_FREE(x, y))
14536   {
14537     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14538                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14539                   ACTION_BREAKING);
14540
14541     // no element can dig solid indestructible elements
14542     if (IS_INDESTRUCTIBLE(element) &&
14543         !IS_DIGGABLE(element) &&
14544         !IS_COLLECTIBLE(element))
14545       return FALSE;
14546
14547     if (AmoebaNr[x][y] &&
14548         (element == EL_AMOEBA_FULL ||
14549          element == EL_BD_AMOEBA ||
14550          element == EL_AMOEBA_GROWING))
14551     {
14552       AmoebaCnt[AmoebaNr[x][y]]--;
14553       AmoebaCnt2[AmoebaNr[x][y]]--;
14554     }
14555
14556     if (IS_MOVING(x, y))
14557       RemoveMovingField(x, y);
14558     else
14559     {
14560       RemoveField(x, y);
14561       TEST_DrawLevelField(x, y);
14562     }
14563
14564     // if digged element was about to explode, prevent the explosion
14565     ExplodeField[x][y] = EX_TYPE_NONE;
14566
14567     PlayLevelSoundAction(x, y, action);
14568   }
14569
14570   Store[x][y] = EL_EMPTY;
14571
14572   // this makes it possible to leave the removed element again
14573   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14574     Store[x][y] = element;
14575
14576   return TRUE;
14577 }
14578
14579 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14580 {
14581   int jx = player->jx, jy = player->jy;
14582   int x = jx + dx, y = jy + dy;
14583   int snap_direction = (dx == -1 ? MV_LEFT  :
14584                         dx == +1 ? MV_RIGHT :
14585                         dy == -1 ? MV_UP    :
14586                         dy == +1 ? MV_DOWN  : MV_NONE);
14587   boolean can_continue_snapping = (level.continuous_snapping &&
14588                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14589
14590   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14591     return FALSE;
14592
14593   if (!player->active || !IN_LEV_FIELD(x, y))
14594     return FALSE;
14595
14596   if (dx && dy)
14597     return FALSE;
14598
14599   if (!dx && !dy)
14600   {
14601     if (player->MovPos == 0)
14602       player->is_pushing = FALSE;
14603
14604     player->is_snapping = FALSE;
14605
14606     if (player->MovPos == 0)
14607     {
14608       player->is_moving = FALSE;
14609       player->is_digging = FALSE;
14610       player->is_collecting = FALSE;
14611     }
14612
14613     return FALSE;
14614   }
14615
14616   // prevent snapping with already pressed snap key when not allowed
14617   if (player->is_snapping && !can_continue_snapping)
14618     return FALSE;
14619
14620   player->MovDir = snap_direction;
14621
14622   if (player->MovPos == 0)
14623   {
14624     player->is_moving = FALSE;
14625     player->is_digging = FALSE;
14626     player->is_collecting = FALSE;
14627   }
14628
14629   player->is_dropping = FALSE;
14630   player->is_dropping_pressed = FALSE;
14631   player->drop_pressed_delay = 0;
14632
14633   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14634     return FALSE;
14635
14636   player->is_snapping = TRUE;
14637   player->is_active = TRUE;
14638
14639   if (player->MovPos == 0)
14640   {
14641     player->is_moving = FALSE;
14642     player->is_digging = FALSE;
14643     player->is_collecting = FALSE;
14644   }
14645
14646   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14647     TEST_DrawLevelField(player->last_jx, player->last_jy);
14648
14649   TEST_DrawLevelField(x, y);
14650
14651   return TRUE;
14652 }
14653
14654 static boolean DropElement(struct PlayerInfo *player)
14655 {
14656   int old_element, new_element;
14657   int dropx = player->jx, dropy = player->jy;
14658   int drop_direction = player->MovDir;
14659   int drop_side = drop_direction;
14660   int drop_element = get_next_dropped_element(player);
14661
14662   /* do not drop an element on top of another element; when holding drop key
14663      pressed without moving, dropped element must move away before the next
14664      element can be dropped (this is especially important if the next element
14665      is dynamite, which can be placed on background for historical reasons) */
14666   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
14667     return MP_ACTION;
14668
14669   if (IS_THROWABLE(drop_element))
14670   {
14671     dropx += GET_DX_FROM_DIR(drop_direction);
14672     dropy += GET_DY_FROM_DIR(drop_direction);
14673
14674     if (!IN_LEV_FIELD(dropx, dropy))
14675       return FALSE;
14676   }
14677
14678   old_element = Tile[dropx][dropy];     // old element at dropping position
14679   new_element = drop_element;           // default: no change when dropping
14680
14681   // check if player is active, not moving and ready to drop
14682   if (!player->active || player->MovPos || player->drop_delay > 0)
14683     return FALSE;
14684
14685   // check if player has anything that can be dropped
14686   if (new_element == EL_UNDEFINED)
14687     return FALSE;
14688
14689   // only set if player has anything that can be dropped
14690   player->is_dropping_pressed = TRUE;
14691
14692   // check if drop key was pressed long enough for EM style dynamite
14693   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14694     return FALSE;
14695
14696   // check if anything can be dropped at the current position
14697   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14698     return FALSE;
14699
14700   // collected custom elements can only be dropped on empty fields
14701   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14702     return FALSE;
14703
14704   if (old_element != EL_EMPTY)
14705     Back[dropx][dropy] = old_element;   // store old element on this field
14706
14707   ResetGfxAnimation(dropx, dropy);
14708   ResetRandomAnimationValue(dropx, dropy);
14709
14710   if (player->inventory_size > 0 ||
14711       player->inventory_infinite_element != EL_UNDEFINED)
14712   {
14713     if (player->inventory_size > 0)
14714     {
14715       player->inventory_size--;
14716
14717       DrawGameDoorValues();
14718
14719       if (new_element == EL_DYNAMITE)
14720         new_element = EL_DYNAMITE_ACTIVE;
14721       else if (new_element == EL_EM_DYNAMITE)
14722         new_element = EL_EM_DYNAMITE_ACTIVE;
14723       else if (new_element == EL_SP_DISK_RED)
14724         new_element = EL_SP_DISK_RED_ACTIVE;
14725     }
14726
14727     Tile[dropx][dropy] = new_element;
14728
14729     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14730       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14731                           el2img(Tile[dropx][dropy]), 0);
14732
14733     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14734
14735     // needed if previous element just changed to "empty" in the last frame
14736     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14737
14738     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14739                                player->index_bit, drop_side);
14740     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14741                                         CE_PLAYER_DROPS_X,
14742                                         player->index_bit, drop_side);
14743
14744     TestIfElementTouchesCustomElement(dropx, dropy);
14745   }
14746   else          // player is dropping a dyna bomb
14747   {
14748     player->dynabombs_left--;
14749
14750     Tile[dropx][dropy] = new_element;
14751
14752     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14753       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14754                           el2img(Tile[dropx][dropy]), 0);
14755
14756     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14757   }
14758
14759   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
14760     InitField_WithBug1(dropx, dropy, FALSE);
14761
14762   new_element = Tile[dropx][dropy];     // element might have changed
14763
14764   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14765       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14766   {
14767     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14768       MovDir[dropx][dropy] = drop_direction;
14769
14770     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14771
14772     // do not cause impact style collision by dropping elements that can fall
14773     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14774   }
14775
14776   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14777   player->is_dropping = TRUE;
14778
14779   player->drop_pressed_delay = 0;
14780   player->is_dropping_pressed = FALSE;
14781
14782   player->drop_x = dropx;
14783   player->drop_y = dropy;
14784
14785   return TRUE;
14786 }
14787
14788 // ----------------------------------------------------------------------------
14789 // game sound playing functions
14790 // ----------------------------------------------------------------------------
14791
14792 static int *loop_sound_frame = NULL;
14793 static int *loop_sound_volume = NULL;
14794
14795 void InitPlayLevelSound(void)
14796 {
14797   int num_sounds = getSoundListSize();
14798
14799   checked_free(loop_sound_frame);
14800   checked_free(loop_sound_volume);
14801
14802   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14803   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14804 }
14805
14806 static void PlayLevelSound(int x, int y, int nr)
14807 {
14808   int sx = SCREENX(x), sy = SCREENY(y);
14809   int volume, stereo_position;
14810   int max_distance = 8;
14811   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14812
14813   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14814       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14815     return;
14816
14817   if (!IN_LEV_FIELD(x, y) ||
14818       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14819       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14820     return;
14821
14822   volume = SOUND_MAX_VOLUME;
14823
14824   if (!IN_SCR_FIELD(sx, sy))
14825   {
14826     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14827     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14828
14829     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14830   }
14831
14832   stereo_position = (SOUND_MAX_LEFT +
14833                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14834                      (SCR_FIELDX + 2 * max_distance));
14835
14836   if (IS_LOOP_SOUND(nr))
14837   {
14838     /* This assures that quieter loop sounds do not overwrite louder ones,
14839        while restarting sound volume comparison with each new game frame. */
14840
14841     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14842       return;
14843
14844     loop_sound_volume[nr] = volume;
14845     loop_sound_frame[nr] = FrameCounter;
14846   }
14847
14848   PlaySoundExt(nr, volume, stereo_position, type);
14849 }
14850
14851 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14852 {
14853   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14854                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14855                  y < LEVELY(BY1) ? LEVELY(BY1) :
14856                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14857                  sound_action);
14858 }
14859
14860 static void PlayLevelSoundAction(int x, int y, int action)
14861 {
14862   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
14863 }
14864
14865 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14866 {
14867   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14868
14869   if (sound_effect != SND_UNDEFINED)
14870     PlayLevelSound(x, y, sound_effect);
14871 }
14872
14873 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14874                                               int action)
14875 {
14876   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14877
14878   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14879     PlayLevelSound(x, y, sound_effect);
14880 }
14881
14882 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14883 {
14884   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
14885
14886   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14887     PlayLevelSound(x, y, sound_effect);
14888 }
14889
14890 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14891 {
14892   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
14893
14894   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14895     StopSound(sound_effect);
14896 }
14897
14898 static int getLevelMusicNr(void)
14899 {
14900   if (levelset.music[level_nr] != MUS_UNDEFINED)
14901     return levelset.music[level_nr];            // from config file
14902   else
14903     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
14904 }
14905
14906 static void FadeLevelSounds(void)
14907 {
14908   FadeSounds();
14909 }
14910
14911 static void FadeLevelMusic(void)
14912 {
14913   int music_nr = getLevelMusicNr();
14914   char *curr_music = getCurrentlyPlayingMusicFilename();
14915   char *next_music = getMusicInfoEntryFilename(music_nr);
14916
14917   if (!strEqual(curr_music, next_music))
14918     FadeMusic();
14919 }
14920
14921 void FadeLevelSoundsAndMusic(void)
14922 {
14923   FadeLevelSounds();
14924   FadeLevelMusic();
14925 }
14926
14927 static void PlayLevelMusic(void)
14928 {
14929   int music_nr = getLevelMusicNr();
14930   char *curr_music = getCurrentlyPlayingMusicFilename();
14931   char *next_music = getMusicInfoEntryFilename(music_nr);
14932
14933   if (!strEqual(curr_music, next_music))
14934     PlayMusicLoop(music_nr);
14935 }
14936
14937 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14938 {
14939   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
14940   int offset = 0;
14941   int x = xx - offset;
14942   int y = yy - offset;
14943
14944   switch (sample)
14945   {
14946     case SOUND_blank:
14947       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14948       break;
14949
14950     case SOUND_roll:
14951       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14952       break;
14953
14954     case SOUND_stone:
14955       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14956       break;
14957
14958     case SOUND_nut:
14959       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14960       break;
14961
14962     case SOUND_crack:
14963       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14964       break;
14965
14966     case SOUND_bug:
14967       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14968       break;
14969
14970     case SOUND_tank:
14971       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14972       break;
14973
14974     case SOUND_android_clone:
14975       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14976       break;
14977
14978     case SOUND_android_move:
14979       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14980       break;
14981
14982     case SOUND_spring:
14983       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14984       break;
14985
14986     case SOUND_slurp:
14987       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14988       break;
14989
14990     case SOUND_eater:
14991       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14992       break;
14993
14994     case SOUND_eater_eat:
14995       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14996       break;
14997
14998     case SOUND_alien:
14999       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15000       break;
15001
15002     case SOUND_collect:
15003       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15004       break;
15005
15006     case SOUND_diamond:
15007       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15008       break;
15009
15010     case SOUND_squash:
15011       // !!! CHECK THIS !!!
15012 #if 1
15013       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15014 #else
15015       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15016 #endif
15017       break;
15018
15019     case SOUND_wonderfall:
15020       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15021       break;
15022
15023     case SOUND_drip:
15024       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15025       break;
15026
15027     case SOUND_push:
15028       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15029       break;
15030
15031     case SOUND_dirt:
15032       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15033       break;
15034
15035     case SOUND_acid:
15036       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15037       break;
15038
15039     case SOUND_ball:
15040       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15041       break;
15042
15043     case SOUND_slide:
15044       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15045       break;
15046
15047     case SOUND_wonder:
15048       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15049       break;
15050
15051     case SOUND_door:
15052       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15053       break;
15054
15055     case SOUND_exit_open:
15056       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15057       break;
15058
15059     case SOUND_exit_leave:
15060       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15061       break;
15062
15063     case SOUND_dynamite:
15064       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15065       break;
15066
15067     case SOUND_tick:
15068       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15069       break;
15070
15071     case SOUND_press:
15072       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15073       break;
15074
15075     case SOUND_wheel:
15076       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15077       break;
15078
15079     case SOUND_boom:
15080       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15081       break;
15082
15083     case SOUND_die:
15084       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15085       break;
15086
15087     case SOUND_time:
15088       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15089       break;
15090
15091     default:
15092       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15093       break;
15094   }
15095 }
15096
15097 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15098 {
15099   int element = map_element_SP_to_RND(element_sp);
15100   int action = map_action_SP_to_RND(action_sp);
15101   int offset = (setup.sp_show_border_elements ? 0 : 1);
15102   int x = xx - offset;
15103   int y = yy - offset;
15104
15105   PlayLevelSoundElementAction(x, y, element, action);
15106 }
15107
15108 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15109 {
15110   int element = map_element_MM_to_RND(element_mm);
15111   int action = map_action_MM_to_RND(action_mm);
15112   int offset = 0;
15113   int x = xx - offset;
15114   int y = yy - offset;
15115
15116   if (!IS_MM_ELEMENT(element))
15117     element = EL_MM_DEFAULT;
15118
15119   PlayLevelSoundElementAction(x, y, element, action);
15120 }
15121
15122 void PlaySound_MM(int sound_mm)
15123 {
15124   int sound = map_sound_MM_to_RND(sound_mm);
15125
15126   if (sound == SND_UNDEFINED)
15127     return;
15128
15129   PlaySound(sound);
15130 }
15131
15132 void PlaySoundLoop_MM(int sound_mm)
15133 {
15134   int sound = map_sound_MM_to_RND(sound_mm);
15135
15136   if (sound == SND_UNDEFINED)
15137     return;
15138
15139   PlaySoundLoop(sound);
15140 }
15141
15142 void StopSound_MM(int sound_mm)
15143 {
15144   int sound = map_sound_MM_to_RND(sound_mm);
15145
15146   if (sound == SND_UNDEFINED)
15147     return;
15148
15149   StopSound(sound);
15150 }
15151
15152 void RaiseScore(int value)
15153 {
15154   game.score += value;
15155
15156   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15157
15158   DisplayGameControlValues();
15159 }
15160
15161 void RaiseScoreElement(int element)
15162 {
15163   switch (element)
15164   {
15165     case EL_EMERALD:
15166     case EL_BD_DIAMOND:
15167     case EL_EMERALD_YELLOW:
15168     case EL_EMERALD_RED:
15169     case EL_EMERALD_PURPLE:
15170     case EL_SP_INFOTRON:
15171       RaiseScore(level.score[SC_EMERALD]);
15172       break;
15173     case EL_DIAMOND:
15174       RaiseScore(level.score[SC_DIAMOND]);
15175       break;
15176     case EL_CRYSTAL:
15177       RaiseScore(level.score[SC_CRYSTAL]);
15178       break;
15179     case EL_PEARL:
15180       RaiseScore(level.score[SC_PEARL]);
15181       break;
15182     case EL_BUG:
15183     case EL_BD_BUTTERFLY:
15184     case EL_SP_ELECTRON:
15185       RaiseScore(level.score[SC_BUG]);
15186       break;
15187     case EL_SPACESHIP:
15188     case EL_BD_FIREFLY:
15189     case EL_SP_SNIKSNAK:
15190       RaiseScore(level.score[SC_SPACESHIP]);
15191       break;
15192     case EL_YAMYAM:
15193     case EL_DARK_YAMYAM:
15194       RaiseScore(level.score[SC_YAMYAM]);
15195       break;
15196     case EL_ROBOT:
15197       RaiseScore(level.score[SC_ROBOT]);
15198       break;
15199     case EL_PACMAN:
15200       RaiseScore(level.score[SC_PACMAN]);
15201       break;
15202     case EL_NUT:
15203       RaiseScore(level.score[SC_NUT]);
15204       break;
15205     case EL_DYNAMITE:
15206     case EL_EM_DYNAMITE:
15207     case EL_SP_DISK_RED:
15208     case EL_DYNABOMB_INCREASE_NUMBER:
15209     case EL_DYNABOMB_INCREASE_SIZE:
15210     case EL_DYNABOMB_INCREASE_POWER:
15211       RaiseScore(level.score[SC_DYNAMITE]);
15212       break;
15213     case EL_SHIELD_NORMAL:
15214     case EL_SHIELD_DEADLY:
15215       RaiseScore(level.score[SC_SHIELD]);
15216       break;
15217     case EL_EXTRA_TIME:
15218       RaiseScore(level.extra_time_score);
15219       break;
15220     case EL_KEY_1:
15221     case EL_KEY_2:
15222     case EL_KEY_3:
15223     case EL_KEY_4:
15224     case EL_EM_KEY_1:
15225     case EL_EM_KEY_2:
15226     case EL_EM_KEY_3:
15227     case EL_EM_KEY_4:
15228     case EL_EMC_KEY_5:
15229     case EL_EMC_KEY_6:
15230     case EL_EMC_KEY_7:
15231     case EL_EMC_KEY_8:
15232     case EL_DC_KEY_WHITE:
15233       RaiseScore(level.score[SC_KEY]);
15234       break;
15235     default:
15236       RaiseScore(element_info[element].collect_score);
15237       break;
15238   }
15239 }
15240
15241 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15242 {
15243   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15244   {
15245     // closing door required in case of envelope style request dialogs
15246     if (!skip_request)
15247     {
15248       // prevent short reactivation of overlay buttons while closing door
15249       SetOverlayActive(FALSE);
15250
15251       CloseDoor(DOOR_CLOSE_1);
15252     }
15253
15254     if (network.enabled)
15255       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15256     else
15257     {
15258       if (quick_quit)
15259         FadeSkipNextFadeIn();
15260
15261       SetGameStatus(GAME_MODE_MAIN);
15262
15263       DrawMainMenu();
15264     }
15265   }
15266   else          // continue playing the game
15267   {
15268     if (tape.playing && tape.deactivate_display)
15269       TapeDeactivateDisplayOff(TRUE);
15270
15271     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15272
15273     if (tape.playing && tape.deactivate_display)
15274       TapeDeactivateDisplayOn();
15275   }
15276 }
15277
15278 void RequestQuitGame(boolean ask_if_really_quit)
15279 {
15280   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15281   boolean skip_request = game.all_players_gone || quick_quit;
15282
15283   RequestQuitGameExt(skip_request, quick_quit,
15284                      "Do you really want to quit the game?");
15285 }
15286
15287 void RequestRestartGame(char *message)
15288 {
15289   game.restart_game_message = NULL;
15290
15291   boolean has_started_game = hasStartedNetworkGame();
15292   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15293
15294   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15295   {
15296     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15297   }
15298   else
15299   {
15300     // needed in case of envelope request to close game panel
15301     CloseDoor(DOOR_CLOSE_1);
15302
15303     SetGameStatus(GAME_MODE_MAIN);
15304
15305     DrawMainMenu();
15306   }
15307 }
15308
15309 void CheckGameOver(void)
15310 {
15311   static boolean last_game_over = FALSE;
15312   static int game_over_delay = 0;
15313   int game_over_delay_value = 50;
15314   boolean game_over = checkGameFailed();
15315
15316   // do not handle game over if request dialog is already active
15317   if (game.request_active)
15318     return;
15319
15320   // do not ask to play again if game was never actually played
15321   if (!game.GamePlayed)
15322     return;
15323
15324   if (!game_over)
15325   {
15326     last_game_over = FALSE;
15327     game_over_delay = game_over_delay_value;
15328
15329     return;
15330   }
15331
15332   if (game_over_delay > 0)
15333   {
15334     game_over_delay--;
15335
15336     return;
15337   }
15338
15339   if (last_game_over != game_over)
15340     game.restart_game_message = (hasStartedNetworkGame() ?
15341                                  "Game over! Play it again?" :
15342                                  "Game over!");
15343
15344   last_game_over = game_over;
15345 }
15346
15347 boolean checkGameSolved(void)
15348 {
15349   // set for all game engines if level was solved
15350   return game.LevelSolved_GameEnd;
15351 }
15352
15353 boolean checkGameFailed(void)
15354 {
15355   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15356     return (game_em.game_over && !game_em.level_solved);
15357   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15358     return (game_sp.game_over && !game_sp.level_solved);
15359   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15360     return (game_mm.game_over && !game_mm.level_solved);
15361   else                          // GAME_ENGINE_TYPE_RND
15362     return (game.GameOver && !game.LevelSolved);
15363 }
15364
15365 boolean checkGameEnded(void)
15366 {
15367   return (checkGameSolved() || checkGameFailed());
15368 }
15369
15370
15371 // ----------------------------------------------------------------------------
15372 // random generator functions
15373 // ----------------------------------------------------------------------------
15374
15375 unsigned int InitEngineRandom_RND(int seed)
15376 {
15377   game.num_random_calls = 0;
15378
15379   return InitEngineRandom(seed);
15380 }
15381
15382 unsigned int RND(int max)
15383 {
15384   if (max > 0)
15385   {
15386     game.num_random_calls++;
15387
15388     return GetEngineRandom(max);
15389   }
15390
15391   return 0;
15392 }
15393
15394
15395 // ----------------------------------------------------------------------------
15396 // game engine snapshot handling functions
15397 // ----------------------------------------------------------------------------
15398
15399 struct EngineSnapshotInfo
15400 {
15401   // runtime values for custom element collect score
15402   int collect_score[NUM_CUSTOM_ELEMENTS];
15403
15404   // runtime values for group element choice position
15405   int choice_pos[NUM_GROUP_ELEMENTS];
15406
15407   // runtime values for belt position animations
15408   int belt_graphic[4][NUM_BELT_PARTS];
15409   int belt_anim_mode[4][NUM_BELT_PARTS];
15410 };
15411
15412 static struct EngineSnapshotInfo engine_snapshot_rnd;
15413 static char *snapshot_level_identifier = NULL;
15414 static int snapshot_level_nr = -1;
15415
15416 static void SaveEngineSnapshotValues_RND(void)
15417 {
15418   static int belt_base_active_element[4] =
15419   {
15420     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15421     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15422     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15423     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15424   };
15425   int i, j;
15426
15427   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15428   {
15429     int element = EL_CUSTOM_START + i;
15430
15431     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15432   }
15433
15434   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15435   {
15436     int element = EL_GROUP_START + i;
15437
15438     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15439   }
15440
15441   for (i = 0; i < 4; i++)
15442   {
15443     for (j = 0; j < NUM_BELT_PARTS; j++)
15444     {
15445       int element = belt_base_active_element[i] + j;
15446       int graphic = el2img(element);
15447       int anim_mode = graphic_info[graphic].anim_mode;
15448
15449       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15450       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15451     }
15452   }
15453 }
15454
15455 static void LoadEngineSnapshotValues_RND(void)
15456 {
15457   unsigned int num_random_calls = game.num_random_calls;
15458   int i, j;
15459
15460   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15461   {
15462     int element = EL_CUSTOM_START + i;
15463
15464     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15465   }
15466
15467   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15468   {
15469     int element = EL_GROUP_START + i;
15470
15471     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15472   }
15473
15474   for (i = 0; i < 4; i++)
15475   {
15476     for (j = 0; j < NUM_BELT_PARTS; j++)
15477     {
15478       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15479       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15480
15481       graphic_info[graphic].anim_mode = anim_mode;
15482     }
15483   }
15484
15485   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15486   {
15487     InitRND(tape.random_seed);
15488     for (i = 0; i < num_random_calls; i++)
15489       RND(1);
15490   }
15491
15492   if (game.num_random_calls != num_random_calls)
15493   {
15494     Error("number of random calls out of sync");
15495     Error("number of random calls should be %d", num_random_calls);
15496     Error("number of random calls is %d", game.num_random_calls);
15497
15498     Fail("this should not happen -- please debug");
15499   }
15500 }
15501
15502 void FreeEngineSnapshotSingle(void)
15503 {
15504   FreeSnapshotSingle();
15505
15506   setString(&snapshot_level_identifier, NULL);
15507   snapshot_level_nr = -1;
15508 }
15509
15510 void FreeEngineSnapshotList(void)
15511 {
15512   FreeSnapshotList();
15513 }
15514
15515 static ListNode *SaveEngineSnapshotBuffers(void)
15516 {
15517   ListNode *buffers = NULL;
15518
15519   // copy some special values to a structure better suited for the snapshot
15520
15521   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15522     SaveEngineSnapshotValues_RND();
15523   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15524     SaveEngineSnapshotValues_EM();
15525   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15526     SaveEngineSnapshotValues_SP(&buffers);
15527   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15528     SaveEngineSnapshotValues_MM(&buffers);
15529
15530   // save values stored in special snapshot structure
15531
15532   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15533     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15534   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15535     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15536   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15537     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15538   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15539     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15540
15541   // save further RND engine values
15542
15543   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15544   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15545   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15546
15547   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15548   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15549   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15550   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15551   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15552
15553   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15554   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15555   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15556
15557   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15558
15559   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15560   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15561
15562   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15563   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15564   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15565   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15566   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15567   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15568   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15569   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15570   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15571   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15572   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15573   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15574   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15575   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15576   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15577   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15578   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15579   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15580
15581   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15582   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15583
15584   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15585   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15586   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15587
15588   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15589   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15590
15591   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15592   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15593   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15594   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15595   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15596
15597   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15598   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15599
15600 #if 0
15601   ListNode *node = engine_snapshot_list_rnd;
15602   int num_bytes = 0;
15603
15604   while (node != NULL)
15605   {
15606     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15607
15608     node = node->next;
15609   }
15610
15611   Debug("game:playing:SaveEngineSnapshotBuffers",
15612         "size of engine snapshot: %d bytes", num_bytes);
15613 #endif
15614
15615   return buffers;
15616 }
15617
15618 void SaveEngineSnapshotSingle(void)
15619 {
15620   ListNode *buffers = SaveEngineSnapshotBuffers();
15621
15622   // finally save all snapshot buffers to single snapshot
15623   SaveSnapshotSingle(buffers);
15624
15625   // save level identification information
15626   setString(&snapshot_level_identifier, leveldir_current->identifier);
15627   snapshot_level_nr = level_nr;
15628 }
15629
15630 boolean CheckSaveEngineSnapshotToList(void)
15631 {
15632   boolean save_snapshot =
15633     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15634      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15635       game.snapshot.changed_action) ||
15636      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15637       game.snapshot.collected_item));
15638
15639   game.snapshot.changed_action = FALSE;
15640   game.snapshot.collected_item = FALSE;
15641   game.snapshot.save_snapshot = save_snapshot;
15642
15643   return save_snapshot;
15644 }
15645
15646 void SaveEngineSnapshotToList(void)
15647 {
15648   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15649       tape.quick_resume)
15650     return;
15651
15652   ListNode *buffers = SaveEngineSnapshotBuffers();
15653
15654   // finally save all snapshot buffers to snapshot list
15655   SaveSnapshotToList(buffers);
15656 }
15657
15658 void SaveEngineSnapshotToListInitial(void)
15659 {
15660   FreeEngineSnapshotList();
15661
15662   SaveEngineSnapshotToList();
15663 }
15664
15665 static void LoadEngineSnapshotValues(void)
15666 {
15667   // restore special values from snapshot structure
15668
15669   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15670     LoadEngineSnapshotValues_RND();
15671   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15672     LoadEngineSnapshotValues_EM();
15673   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15674     LoadEngineSnapshotValues_SP();
15675   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15676     LoadEngineSnapshotValues_MM();
15677 }
15678
15679 void LoadEngineSnapshotSingle(void)
15680 {
15681   LoadSnapshotSingle();
15682
15683   LoadEngineSnapshotValues();
15684 }
15685
15686 static void LoadEngineSnapshot_Undo(int steps)
15687 {
15688   LoadSnapshotFromList_Older(steps);
15689
15690   LoadEngineSnapshotValues();
15691 }
15692
15693 static void LoadEngineSnapshot_Redo(int steps)
15694 {
15695   LoadSnapshotFromList_Newer(steps);
15696
15697   LoadEngineSnapshotValues();
15698 }
15699
15700 boolean CheckEngineSnapshotSingle(void)
15701 {
15702   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15703           snapshot_level_nr == level_nr);
15704 }
15705
15706 boolean CheckEngineSnapshotList(void)
15707 {
15708   return CheckSnapshotList();
15709 }
15710
15711
15712 // ---------- new game button stuff -------------------------------------------
15713
15714 static struct
15715 {
15716   int graphic;
15717   struct XY *pos;
15718   int gadget_id;
15719   boolean *setup_value;
15720   boolean allowed_on_tape;
15721   boolean is_touch_button;
15722   char *infotext;
15723 } gamebutton_info[NUM_GAME_BUTTONS] =
15724 {
15725   {
15726     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15727     GAME_CTRL_ID_STOP,                          NULL,
15728     TRUE, FALSE,                                "stop game"
15729   },
15730   {
15731     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15732     GAME_CTRL_ID_PAUSE,                         NULL,
15733     TRUE, FALSE,                                "pause game"
15734   },
15735   {
15736     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15737     GAME_CTRL_ID_PLAY,                          NULL,
15738     TRUE, FALSE,                                "play game"
15739   },
15740   {
15741     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15742     GAME_CTRL_ID_UNDO,                          NULL,
15743     TRUE, FALSE,                                "undo step"
15744   },
15745   {
15746     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15747     GAME_CTRL_ID_REDO,                          NULL,
15748     TRUE, FALSE,                                "redo step"
15749   },
15750   {
15751     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15752     GAME_CTRL_ID_SAVE,                          NULL,
15753     TRUE, FALSE,                                "save game"
15754   },
15755   {
15756     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15757     GAME_CTRL_ID_PAUSE2,                        NULL,
15758     TRUE, FALSE,                                "pause game"
15759   },
15760   {
15761     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15762     GAME_CTRL_ID_LOAD,                          NULL,
15763     TRUE, FALSE,                                "load game"
15764   },
15765   {
15766     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15767     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15768     FALSE, FALSE,                               "stop game"
15769   },
15770   {
15771     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15772     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15773     FALSE, FALSE,                               "pause game"
15774   },
15775   {
15776     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15777     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15778     FALSE, FALSE,                               "play game"
15779   },
15780   {
15781     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
15782     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
15783     FALSE, TRUE,                                "stop game"
15784   },
15785   {
15786     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
15787     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
15788     FALSE, TRUE,                                "pause game"
15789   },
15790   {
15791     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15792     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15793     TRUE, FALSE,                                "background music on/off"
15794   },
15795   {
15796     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15797     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15798     TRUE, FALSE,                                "sound loops on/off"
15799   },
15800   {
15801     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15802     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15803     TRUE, FALSE,                                "normal sounds on/off"
15804   },
15805   {
15806     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15807     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15808     FALSE, FALSE,                               "background music on/off"
15809   },
15810   {
15811     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15812     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15813     FALSE, FALSE,                               "sound loops on/off"
15814   },
15815   {
15816     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15817     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15818     FALSE, FALSE,                               "normal sounds on/off"
15819   }
15820 };
15821
15822 void CreateGameButtons(void)
15823 {
15824   int i;
15825
15826   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15827   {
15828     int graphic = gamebutton_info[i].graphic;
15829     struct GraphicInfo *gfx = &graphic_info[graphic];
15830     struct XY *pos = gamebutton_info[i].pos;
15831     struct GadgetInfo *gi;
15832     int button_type;
15833     boolean checked;
15834     unsigned int event_mask;
15835     boolean is_touch_button = gamebutton_info[i].is_touch_button;
15836     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15837     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15838     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
15839     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
15840     int gd_x   = gfx->src_x;
15841     int gd_y   = gfx->src_y;
15842     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15843     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15844     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15845     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15846     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15847     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15848     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
15849     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
15850     int id = i;
15851
15852     if (gfx->bitmap == NULL)
15853     {
15854       game_gadget[id] = NULL;
15855
15856       continue;
15857     }
15858
15859     if (id == GAME_CTRL_ID_STOP ||
15860         id == GAME_CTRL_ID_PANEL_STOP ||
15861         id == GAME_CTRL_ID_TOUCH_STOP ||
15862         id == GAME_CTRL_ID_PLAY ||
15863         id == GAME_CTRL_ID_PANEL_PLAY ||
15864         id == GAME_CTRL_ID_SAVE ||
15865         id == GAME_CTRL_ID_LOAD)
15866     {
15867       button_type = GD_TYPE_NORMAL_BUTTON;
15868       checked = FALSE;
15869       event_mask = GD_EVENT_RELEASED;
15870     }
15871     else if (id == GAME_CTRL_ID_UNDO ||
15872              id == GAME_CTRL_ID_REDO)
15873     {
15874       button_type = GD_TYPE_NORMAL_BUTTON;
15875       checked = FALSE;
15876       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15877     }
15878     else
15879     {
15880       button_type = GD_TYPE_CHECK_BUTTON;
15881       checked = (gamebutton_info[i].setup_value != NULL ?
15882                  *gamebutton_info[i].setup_value : FALSE);
15883       event_mask = GD_EVENT_PRESSED;
15884     }
15885
15886     gi = CreateGadget(GDI_CUSTOM_ID, id,
15887                       GDI_IMAGE_ID, graphic,
15888                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15889                       GDI_X, base_x + x,
15890                       GDI_Y, base_y + y,
15891                       GDI_WIDTH, gfx->width,
15892                       GDI_HEIGHT, gfx->height,
15893                       GDI_TYPE, button_type,
15894                       GDI_STATE, GD_BUTTON_UNPRESSED,
15895                       GDI_CHECKED, checked,
15896                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15897                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15898                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15899                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15900                       GDI_DIRECT_DRAW, FALSE,
15901                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
15902                       GDI_EVENT_MASK, event_mask,
15903                       GDI_CALLBACK_ACTION, HandleGameButtons,
15904                       GDI_END);
15905
15906     if (gi == NULL)
15907       Fail("cannot create gadget");
15908
15909     game_gadget[id] = gi;
15910   }
15911 }
15912
15913 void FreeGameButtons(void)
15914 {
15915   int i;
15916
15917   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15918     FreeGadget(game_gadget[i]);
15919 }
15920
15921 static void UnmapGameButtonsAtSamePosition(int id)
15922 {
15923   int i;
15924
15925   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15926     if (i != id &&
15927         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15928         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15929       UnmapGadget(game_gadget[i]);
15930 }
15931
15932 static void UnmapGameButtonsAtSamePosition_All(void)
15933 {
15934   if (setup.show_snapshot_buttons)
15935   {
15936     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15937     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15938     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15939   }
15940   else
15941   {
15942     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15943     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15944     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15945
15946     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15947     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15948     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15949   }
15950 }
15951
15952 static void MapGameButtonsAtSamePosition(int id)
15953 {
15954   int i;
15955
15956   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15957     if (i != id &&
15958         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15959         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15960       MapGadget(game_gadget[i]);
15961
15962   UnmapGameButtonsAtSamePosition_All();
15963 }
15964
15965 void MapUndoRedoButtons(void)
15966 {
15967   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15968   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15969
15970   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15971   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15972 }
15973
15974 void UnmapUndoRedoButtons(void)
15975 {
15976   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15977   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15978
15979   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15980   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15981 }
15982
15983 void ModifyPauseButtons(void)
15984 {
15985   static int ids[] =
15986   {
15987     GAME_CTRL_ID_PAUSE,
15988     GAME_CTRL_ID_PAUSE2,
15989     GAME_CTRL_ID_PANEL_PAUSE,
15990     GAME_CTRL_ID_TOUCH_PAUSE,
15991     -1
15992   };
15993   int i;
15994
15995   for (i = 0; ids[i] > -1; i++)
15996     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
15997 }
15998
15999 static void MapGameButtonsExt(boolean on_tape)
16000 {
16001   int i;
16002
16003   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16004     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
16005         i != GAME_CTRL_ID_UNDO &&
16006         i != GAME_CTRL_ID_REDO)
16007       MapGadget(game_gadget[i]);
16008
16009   UnmapGameButtonsAtSamePosition_All();
16010
16011   RedrawGameButtons();
16012 }
16013
16014 static void UnmapGameButtonsExt(boolean on_tape)
16015 {
16016   int i;
16017
16018   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16019     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16020       UnmapGadget(game_gadget[i]);
16021 }
16022
16023 static void RedrawGameButtonsExt(boolean on_tape)
16024 {
16025   int i;
16026
16027   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16028     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16029       RedrawGadget(game_gadget[i]);
16030 }
16031
16032 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16033 {
16034   if (gi == NULL)
16035     return;
16036
16037   gi->checked = state;
16038 }
16039
16040 static void RedrawSoundButtonGadget(int id)
16041 {
16042   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16043              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16044              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16045              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16046              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16047              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16048              id);
16049
16050   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16051   RedrawGadget(game_gadget[id2]);
16052 }
16053
16054 void MapGameButtons(void)
16055 {
16056   MapGameButtonsExt(FALSE);
16057 }
16058
16059 void UnmapGameButtons(void)
16060 {
16061   UnmapGameButtonsExt(FALSE);
16062 }
16063
16064 void RedrawGameButtons(void)
16065 {
16066   RedrawGameButtonsExt(FALSE);
16067 }
16068
16069 void MapGameButtonsOnTape(void)
16070 {
16071   MapGameButtonsExt(TRUE);
16072 }
16073
16074 void UnmapGameButtonsOnTape(void)
16075 {
16076   UnmapGameButtonsExt(TRUE);
16077 }
16078
16079 void RedrawGameButtonsOnTape(void)
16080 {
16081   RedrawGameButtonsExt(TRUE);
16082 }
16083
16084 static void GameUndoRedoExt(void)
16085 {
16086   ClearPlayerAction();
16087
16088   tape.pausing = TRUE;
16089
16090   RedrawPlayfield();
16091   UpdateAndDisplayGameControlValues();
16092
16093   DrawCompleteVideoDisplay();
16094   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16095   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16096   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16097
16098   BackToFront();
16099 }
16100
16101 static void GameUndo(int steps)
16102 {
16103   if (!CheckEngineSnapshotList())
16104     return;
16105
16106   LoadEngineSnapshot_Undo(steps);
16107
16108   GameUndoRedoExt();
16109 }
16110
16111 static void GameRedo(int steps)
16112 {
16113   if (!CheckEngineSnapshotList())
16114     return;
16115
16116   LoadEngineSnapshot_Redo(steps);
16117
16118   GameUndoRedoExt();
16119 }
16120
16121 static void HandleGameButtonsExt(int id, int button)
16122 {
16123   static boolean game_undo_executed = FALSE;
16124   int steps = BUTTON_STEPSIZE(button);
16125   boolean handle_game_buttons =
16126     (game_status == GAME_MODE_PLAYING ||
16127      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16128
16129   if (!handle_game_buttons)
16130     return;
16131
16132   switch (id)
16133   {
16134     case GAME_CTRL_ID_STOP:
16135     case GAME_CTRL_ID_PANEL_STOP:
16136     case GAME_CTRL_ID_TOUCH_STOP:
16137       if (game_status == GAME_MODE_MAIN)
16138         break;
16139
16140       if (tape.playing)
16141         TapeStop();
16142       else
16143         RequestQuitGame(TRUE);
16144
16145       break;
16146
16147     case GAME_CTRL_ID_PAUSE:
16148     case GAME_CTRL_ID_PAUSE2:
16149     case GAME_CTRL_ID_PANEL_PAUSE:
16150     case GAME_CTRL_ID_TOUCH_PAUSE:
16151       if (network.enabled && game_status == GAME_MODE_PLAYING)
16152       {
16153         if (tape.pausing)
16154           SendToServer_ContinuePlaying();
16155         else
16156           SendToServer_PausePlaying();
16157       }
16158       else
16159         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16160
16161       game_undo_executed = FALSE;
16162
16163       break;
16164
16165     case GAME_CTRL_ID_PLAY:
16166     case GAME_CTRL_ID_PANEL_PLAY:
16167       if (game_status == GAME_MODE_MAIN)
16168       {
16169         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16170       }
16171       else if (tape.pausing)
16172       {
16173         if (network.enabled)
16174           SendToServer_ContinuePlaying();
16175         else
16176           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16177       }
16178       break;
16179
16180     case GAME_CTRL_ID_UNDO:
16181       // Important: When using "save snapshot when collecting an item" mode,
16182       // load last (current) snapshot for first "undo" after pressing "pause"
16183       // (else the last-but-one snapshot would be loaded, because the snapshot
16184       // pointer already points to the last snapshot when pressing "pause",
16185       // which is fine for "every step/move" mode, but not for "every collect")
16186       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16187           !game_undo_executed)
16188         steps--;
16189
16190       game_undo_executed = TRUE;
16191
16192       GameUndo(steps);
16193       break;
16194
16195     case GAME_CTRL_ID_REDO:
16196       GameRedo(steps);
16197       break;
16198
16199     case GAME_CTRL_ID_SAVE:
16200       TapeQuickSave();
16201       break;
16202
16203     case GAME_CTRL_ID_LOAD:
16204       TapeQuickLoad();
16205       break;
16206
16207     case SOUND_CTRL_ID_MUSIC:
16208     case SOUND_CTRL_ID_PANEL_MUSIC:
16209       if (setup.sound_music)
16210       { 
16211         setup.sound_music = FALSE;
16212
16213         FadeMusic();
16214       }
16215       else if (audio.music_available)
16216       { 
16217         setup.sound = setup.sound_music = TRUE;
16218
16219         SetAudioMode(setup.sound);
16220
16221         if (game_status == GAME_MODE_PLAYING)
16222           PlayLevelMusic();
16223       }
16224
16225       RedrawSoundButtonGadget(id);
16226
16227       break;
16228
16229     case SOUND_CTRL_ID_LOOPS:
16230     case SOUND_CTRL_ID_PANEL_LOOPS:
16231       if (setup.sound_loops)
16232         setup.sound_loops = FALSE;
16233       else if (audio.loops_available)
16234       {
16235         setup.sound = setup.sound_loops = TRUE;
16236
16237         SetAudioMode(setup.sound);
16238       }
16239
16240       RedrawSoundButtonGadget(id);
16241
16242       break;
16243
16244     case SOUND_CTRL_ID_SIMPLE:
16245     case SOUND_CTRL_ID_PANEL_SIMPLE:
16246       if (setup.sound_simple)
16247         setup.sound_simple = FALSE;
16248       else if (audio.sound_available)
16249       {
16250         setup.sound = setup.sound_simple = TRUE;
16251
16252         SetAudioMode(setup.sound);
16253       }
16254
16255       RedrawSoundButtonGadget(id);
16256
16257       break;
16258
16259     default:
16260       break;
16261   }
16262 }
16263
16264 static void HandleGameButtons(struct GadgetInfo *gi)
16265 {
16266   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16267 }
16268
16269 void HandleSoundButtonKeys(Key key)
16270 {
16271   if (key == setup.shortcut.sound_simple)
16272     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16273   else if (key == setup.shortcut.sound_loops)
16274     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16275   else if (key == setup.shortcut.sound_music)
16276     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16277 }