0d28b40b2961e8774cd2cdea2d4f47feb68aa263
[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
2255   UpdatePlayfieldElementCount();
2256
2257   // update game panel control values
2258
2259   // used instead of "level_nr" (for network games)
2260   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2261   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2262
2263   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2264   for (i = 0; i < MAX_NUM_KEYS; i++)
2265     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2266   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2267   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2268
2269   if (game.centered_player_nr == -1)
2270   {
2271     for (i = 0; i < MAX_PLAYERS; i++)
2272     {
2273       // only one player in Supaplex game engine
2274       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2275         break;
2276
2277       for (k = 0; k < MAX_NUM_KEYS; k++)
2278       {
2279         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2280         {
2281           if (game_em.ply[i]->keys & (1 << k))
2282             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2283               get_key_element_from_nr(k);
2284         }
2285         else if (stored_player[i].key[k])
2286           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2287             get_key_element_from_nr(k);
2288       }
2289
2290       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2291         getPlayerInventorySize(i);
2292
2293       if (stored_player[i].num_white_keys > 0)
2294         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2295           EL_DC_KEY_WHITE;
2296
2297       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2298         stored_player[i].num_white_keys;
2299     }
2300   }
2301   else
2302   {
2303     int player_nr = game.centered_player_nr;
2304
2305     for (k = 0; k < MAX_NUM_KEYS; k++)
2306     {
2307       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2308       {
2309         if (game_em.ply[player_nr]->keys & (1 << k))
2310           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2311             get_key_element_from_nr(k);
2312       }
2313       else if (stored_player[player_nr].key[k])
2314         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2315           get_key_element_from_nr(k);
2316     }
2317
2318     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2319       getPlayerInventorySize(player_nr);
2320
2321     if (stored_player[player_nr].num_white_keys > 0)
2322       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2323
2324     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2325       stored_player[player_nr].num_white_keys;
2326   }
2327
2328   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2329   {
2330     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2331       get_inventory_element_from_pos(local_player, i);
2332     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2333       get_inventory_element_from_pos(local_player, -i - 1);
2334   }
2335
2336   game_panel_controls[GAME_PANEL_SCORE].value = score;
2337   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2338
2339   game_panel_controls[GAME_PANEL_TIME].value = time;
2340
2341   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2342   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2343   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2344
2345   if (level.time == 0)
2346     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2347   else
2348     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2349
2350   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2351   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2352
2353   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2354
2355   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2356     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2357      EL_EMPTY);
2358   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2359     local_player->shield_normal_time_left;
2360   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2361     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2362      EL_EMPTY);
2363   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2364     local_player->shield_deadly_time_left;
2365
2366   game_panel_controls[GAME_PANEL_EXIT].value =
2367     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2368
2369   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2370     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2371   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2372     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2373      EL_EMC_MAGIC_BALL_SWITCH);
2374
2375   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2376     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2377   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2378     game.light_time_left;
2379
2380   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2381     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2382   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2383     game.timegate_time_left;
2384
2385   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2386     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2387
2388   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2389     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2390   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2391     game.lenses_time_left;
2392
2393   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2394     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2395   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2396     game.magnify_time_left;
2397
2398   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2399     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2400      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2401      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2402      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2403      EL_BALLOON_SWITCH_NONE);
2404
2405   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2406     local_player->dynabomb_count;
2407   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2408     local_player->dynabomb_size;
2409   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2410     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2411
2412   game_panel_controls[GAME_PANEL_PENGUINS].value =
2413     game.friends_still_needed;
2414
2415   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2416     game.sokoban_objects_still_needed;
2417   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2418     game.sokoban_fields_still_needed;
2419
2420   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2421     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2422
2423   for (i = 0; i < NUM_BELTS; i++)
2424   {
2425     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2426       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2427        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2428     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2429       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2430   }
2431
2432   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2433     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2434   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2435     game.magic_wall_time_left;
2436
2437   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2438     local_player->gravity;
2439
2440   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2441     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2442
2443   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2444     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2445       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2446        game.panel.element[i].id : EL_UNDEFINED);
2447
2448   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2449     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2450       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2451        element_info[game.panel.element_count[i].id].element_count : 0);
2452
2453   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2454     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2455       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2456        element_info[game.panel.ce_score[i].id].collect_score : 0);
2457
2458   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2459     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2460       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2461        element_info[game.panel.ce_score_element[i].id].collect_score :
2462        EL_UNDEFINED);
2463
2464   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2465   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2466   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2467
2468   // update game panel control frames
2469
2470   for (i = 0; game_panel_controls[i].nr != -1; i++)
2471   {
2472     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2473
2474     if (gpc->type == TYPE_ELEMENT)
2475     {
2476       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2477       {
2478         int last_anim_random_frame = gfx.anim_random_frame;
2479         int element = gpc->value;
2480         int graphic = el2panelimg(element);
2481
2482         if (gpc->value != gpc->last_value)
2483         {
2484           gpc->gfx_frame = 0;
2485           gpc->gfx_random = INIT_GFX_RANDOM();
2486         }
2487         else
2488         {
2489           gpc->gfx_frame++;
2490
2491           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2492               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2493             gpc->gfx_random = INIT_GFX_RANDOM();
2494         }
2495
2496         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2497           gfx.anim_random_frame = gpc->gfx_random;
2498
2499         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2500           gpc->gfx_frame = element_info[element].collect_score;
2501
2502         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2503                                               gpc->gfx_frame);
2504
2505         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2506           gfx.anim_random_frame = last_anim_random_frame;
2507       }
2508     }
2509     else if (gpc->type == TYPE_GRAPHIC)
2510     {
2511       if (gpc->graphic != IMG_UNDEFINED)
2512       {
2513         int last_anim_random_frame = gfx.anim_random_frame;
2514         int graphic = gpc->graphic;
2515
2516         if (gpc->value != gpc->last_value)
2517         {
2518           gpc->gfx_frame = 0;
2519           gpc->gfx_random = INIT_GFX_RANDOM();
2520         }
2521         else
2522         {
2523           gpc->gfx_frame++;
2524
2525           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2526               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2527             gpc->gfx_random = INIT_GFX_RANDOM();
2528         }
2529
2530         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2531           gfx.anim_random_frame = gpc->gfx_random;
2532
2533         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2534
2535         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2536           gfx.anim_random_frame = last_anim_random_frame;
2537       }
2538     }
2539   }
2540 }
2541
2542 static void DisplayGameControlValues(void)
2543 {
2544   boolean redraw_panel = FALSE;
2545   int i;
2546
2547   for (i = 0; game_panel_controls[i].nr != -1; i++)
2548   {
2549     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2550
2551     if (PANEL_DEACTIVATED(gpc->pos))
2552       continue;
2553
2554     if (gpc->value == gpc->last_value &&
2555         gpc->frame == gpc->last_frame)
2556       continue;
2557
2558     redraw_panel = TRUE;
2559   }
2560
2561   if (!redraw_panel)
2562     return;
2563
2564   // copy default game door content to main double buffer
2565
2566   // !!! CHECK AGAIN !!!
2567   SetPanelBackground();
2568   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2569   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2570
2571   // redraw game control buttons
2572   RedrawGameButtons();
2573
2574   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2575
2576   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2577   {
2578     int nr = game_panel_order[i].nr;
2579     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2580     struct TextPosInfo *pos = gpc->pos;
2581     int type = gpc->type;
2582     int value = gpc->value;
2583     int frame = gpc->frame;
2584     int size = pos->size;
2585     int font = pos->font;
2586     boolean draw_masked = pos->draw_masked;
2587     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2588
2589     if (PANEL_DEACTIVATED(pos))
2590       continue;
2591
2592     gpc->last_value = value;
2593     gpc->last_frame = frame;
2594
2595     if (type == TYPE_INTEGER)
2596     {
2597       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2598           nr == GAME_PANEL_TIME)
2599       {
2600         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2601
2602         if (use_dynamic_size)           // use dynamic number of digits
2603         {
2604           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2605           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2606           int size2 = size1 + 1;
2607           int font1 = pos->font;
2608           int font2 = pos->font_alt;
2609
2610           size = (value < value_change ? size1 : size2);
2611           font = (value < value_change ? font1 : font2);
2612         }
2613       }
2614
2615       // correct text size if "digits" is zero or less
2616       if (size <= 0)
2617         size = strlen(int2str(value, size));
2618
2619       // dynamically correct text alignment
2620       pos->width = size * getFontWidth(font);
2621
2622       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2623                   int2str(value, size), font, mask_mode);
2624     }
2625     else if (type == TYPE_ELEMENT)
2626     {
2627       int element, graphic;
2628       Bitmap *src_bitmap;
2629       int src_x, src_y;
2630       int width, height;
2631       int dst_x = PANEL_XPOS(pos);
2632       int dst_y = PANEL_YPOS(pos);
2633
2634       if (value != EL_UNDEFINED && value != EL_EMPTY)
2635       {
2636         element = value;
2637         graphic = el2panelimg(value);
2638
2639 #if 0
2640         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2641               element, EL_NAME(element), size);
2642 #endif
2643
2644         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2645           size = TILESIZE;
2646
2647         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2648                               &src_x, &src_y);
2649
2650         width  = graphic_info[graphic].width  * size / TILESIZE;
2651         height = graphic_info[graphic].height * size / TILESIZE;
2652
2653         if (draw_masked)
2654           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2655                            dst_x, dst_y);
2656         else
2657           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2658                      dst_x, dst_y);
2659       }
2660     }
2661     else if (type == TYPE_GRAPHIC)
2662     {
2663       int graphic        = gpc->graphic;
2664       int graphic_active = gpc->graphic_active;
2665       Bitmap *src_bitmap;
2666       int src_x, src_y;
2667       int width, height;
2668       int dst_x = PANEL_XPOS(pos);
2669       int dst_y = PANEL_YPOS(pos);
2670       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2671                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2672
2673       if (graphic != IMG_UNDEFINED && !skip)
2674       {
2675         if (pos->style == STYLE_REVERSE)
2676           value = 100 - value;
2677
2678         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2679
2680         if (pos->direction & MV_HORIZONTAL)
2681         {
2682           width  = graphic_info[graphic_active].width * value / 100;
2683           height = graphic_info[graphic_active].height;
2684
2685           if (pos->direction == MV_LEFT)
2686           {
2687             src_x += graphic_info[graphic_active].width - width;
2688             dst_x += graphic_info[graphic_active].width - width;
2689           }
2690         }
2691         else
2692         {
2693           width  = graphic_info[graphic_active].width;
2694           height = graphic_info[graphic_active].height * value / 100;
2695
2696           if (pos->direction == MV_UP)
2697           {
2698             src_y += graphic_info[graphic_active].height - height;
2699             dst_y += graphic_info[graphic_active].height - height;
2700           }
2701         }
2702
2703         if (draw_masked)
2704           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2705                            dst_x, dst_y);
2706         else
2707           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2708                      dst_x, dst_y);
2709
2710         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2711
2712         if (pos->direction & MV_HORIZONTAL)
2713         {
2714           if (pos->direction == MV_RIGHT)
2715           {
2716             src_x += width;
2717             dst_x += width;
2718           }
2719           else
2720           {
2721             dst_x = PANEL_XPOS(pos);
2722           }
2723
2724           width = graphic_info[graphic].width - width;
2725         }
2726         else
2727         {
2728           if (pos->direction == MV_DOWN)
2729           {
2730             src_y += height;
2731             dst_y += height;
2732           }
2733           else
2734           {
2735             dst_y = PANEL_YPOS(pos);
2736           }
2737
2738           height = graphic_info[graphic].height - height;
2739         }
2740
2741         if (draw_masked)
2742           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2743                            dst_x, dst_y);
2744         else
2745           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2746                      dst_x, dst_y);
2747       }
2748     }
2749     else if (type == TYPE_STRING)
2750     {
2751       boolean active = (value != 0);
2752       char *state_normal = "off";
2753       char *state_active = "on";
2754       char *state = (active ? state_active : state_normal);
2755       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2756                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2757                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2758                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2759
2760       if (nr == GAME_PANEL_GRAVITY_STATE)
2761       {
2762         int font1 = pos->font;          // (used for normal state)
2763         int font2 = pos->font_alt;      // (used for active state)
2764
2765         font = (active ? font2 : font1);
2766       }
2767
2768       if (s != NULL)
2769       {
2770         char *s_cut;
2771
2772         if (size <= 0)
2773         {
2774           // don't truncate output if "chars" is zero or less
2775           size = strlen(s);
2776
2777           // dynamically correct text alignment
2778           pos->width = size * getFontWidth(font);
2779         }
2780
2781         s_cut = getStringCopyN(s, size);
2782
2783         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2784                     s_cut, font, mask_mode);
2785
2786         free(s_cut);
2787       }
2788     }
2789
2790     redraw_mask |= REDRAW_DOOR_1;
2791   }
2792
2793   SetGameStatus(GAME_MODE_PLAYING);
2794 }
2795
2796 void UpdateAndDisplayGameControlValues(void)
2797 {
2798   if (tape.deactivate_display)
2799     return;
2800
2801   UpdateGameControlValues();
2802   DisplayGameControlValues();
2803 }
2804
2805 #if 0
2806 static void UpdateGameDoorValues(void)
2807 {
2808   UpdateGameControlValues();
2809 }
2810 #endif
2811
2812 void DrawGameDoorValues(void)
2813 {
2814   DisplayGameControlValues();
2815 }
2816
2817
2818 // ============================================================================
2819 // InitGameEngine()
2820 // ----------------------------------------------------------------------------
2821 // initialize game engine due to level / tape version number
2822 // ============================================================================
2823
2824 static void InitGameEngine(void)
2825 {
2826   int i, j, k, l, x, y;
2827
2828   // set game engine from tape file when re-playing, else from level file
2829   game.engine_version = (tape.playing ? tape.engine_version :
2830                          level.game_version);
2831
2832   // set single or multi-player game mode (needed for re-playing tapes)
2833   game.team_mode = setup.team_mode;
2834
2835   if (tape.playing)
2836   {
2837     int num_players = 0;
2838
2839     for (i = 0; i < MAX_PLAYERS; i++)
2840       if (tape.player_participates[i])
2841         num_players++;
2842
2843     // multi-player tapes contain input data for more than one player
2844     game.team_mode = (num_players > 1);
2845   }
2846
2847 #if 0
2848   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2849         level.game_version);
2850   Debug("game:init:level", "          tape.file_version   == %06d",
2851         tape.file_version);
2852   Debug("game:init:level", "          tape.game_version   == %06d",
2853         tape.game_version);
2854   Debug("game:init:level", "          tape.engine_version == %06d",
2855         tape.engine_version);
2856   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2857         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2858 #endif
2859
2860   // --------------------------------------------------------------------------
2861   // set flags for bugs and changes according to active game engine version
2862   // --------------------------------------------------------------------------
2863
2864   /*
2865     Summary of bugfix:
2866     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2867
2868     Bug was introduced in version:
2869     2.0.1
2870
2871     Bug was fixed in version:
2872     4.2.0.0
2873
2874     Description:
2875     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2876     but the property "can fall" was missing, which caused some levels to be
2877     unsolvable. This was fixed in version 4.2.0.0.
2878
2879     Affected levels/tapes:
2880     An example for a tape that was fixed by this bugfix is tape 029 from the
2881     level set "rnd_sam_bateman".
2882     The wrong behaviour will still be used for all levels or tapes that were
2883     created/recorded with it. An example for this is tape 023 from the level
2884     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2885   */
2886
2887   boolean use_amoeba_dropping_cannot_fall_bug =
2888     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2889       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2890      (tape.playing &&
2891       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2892       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2893
2894   /*
2895     Summary of bugfix/change:
2896     Fixed move speed of elements entering or leaving magic wall.
2897
2898     Fixed/changed in version:
2899     2.0.1
2900
2901     Description:
2902     Before 2.0.1, move speed of elements entering or leaving magic wall was
2903     twice as fast as it is now.
2904     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2905
2906     Affected levels/tapes:
2907     The first condition is generally needed for all levels/tapes before version
2908     2.0.1, which might use the old behaviour before it was changed; known tapes
2909     that are affected: Tape 014 from the level set "rnd_conor_mancone".
2910     The second condition is an exception from the above case and is needed for
2911     the special case of tapes recorded with game (not engine!) version 2.0.1 or
2912     above, but before it was known that this change would break tapes like the
2913     above and was fixed in 4.2.0.0, so that the changed behaviour was active
2914     although the engine version while recording maybe was before 2.0.1. There
2915     are a lot of tapes that are affected by this exception, like tape 006 from
2916     the level set "rnd_conor_mancone".
2917   */
2918
2919   boolean use_old_move_stepsize_for_magic_wall =
2920     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
2921      !(tape.playing &&
2922        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2923        tape.game_version <  VERSION_IDENT(4,2,0,0)));
2924
2925   /*
2926     Summary of bugfix/change:
2927     Fixed handling for custom elements that change when pushed by the player.
2928
2929     Fixed/changed in version:
2930     3.1.0
2931
2932     Description:
2933     Before 3.1.0, custom elements that "change when pushing" changed directly
2934     after the player started pushing them (until then handled in "DigField()").
2935     Since 3.1.0, these custom elements are not changed until the "pushing"
2936     move of the element is finished (now handled in "ContinueMoving()").
2937
2938     Affected levels/tapes:
2939     The first condition is generally needed for all levels/tapes before version
2940     3.1.0, which might use the old behaviour before it was changed; known tapes
2941     that are affected are some tapes from the level set "Walpurgis Gardens" by
2942     Jamie Cullen.
2943     The second condition is an exception from the above case and is needed for
2944     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2945     above (including some development versions of 3.1.0), but before it was
2946     known that this change would break tapes like the above and was fixed in
2947     3.1.1, so that the changed behaviour was active although the engine version
2948     while recording maybe was before 3.1.0. There is at least one tape that is
2949     affected by this exception, which is the tape for the one-level set "Bug
2950     Machine" by Juergen Bonhagen.
2951   */
2952
2953   game.use_change_when_pushing_bug =
2954     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2955      !(tape.playing &&
2956        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2957        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2958
2959   /*
2960     Summary of bugfix/change:
2961     Fixed handling for blocking the field the player leaves when moving.
2962
2963     Fixed/changed in version:
2964     3.1.1
2965
2966     Description:
2967     Before 3.1.1, when "block last field when moving" was enabled, the field
2968     the player is leaving when moving was blocked for the time of the move,
2969     and was directly unblocked afterwards. This resulted in the last field
2970     being blocked for exactly one less than the number of frames of one player
2971     move. Additionally, even when blocking was disabled, the last field was
2972     blocked for exactly one frame.
2973     Since 3.1.1, due to changes in player movement handling, the last field
2974     is not blocked at all when blocking is disabled. When blocking is enabled,
2975     the last field is blocked for exactly the number of frames of one player
2976     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2977     last field is blocked for exactly one more than the number of frames of
2978     one player move.
2979
2980     Affected levels/tapes:
2981     (!!! yet to be determined -- probably many !!!)
2982   */
2983
2984   game.use_block_last_field_bug =
2985     (game.engine_version < VERSION_IDENT(3,1,1,0));
2986
2987   /* various special flags and settings for native Emerald Mine game engine */
2988
2989   game_em.use_single_button =
2990     (game.engine_version > VERSION_IDENT(4,0,0,2));
2991
2992   game_em.use_snap_key_bug =
2993     (game.engine_version < VERSION_IDENT(4,0,1,0));
2994
2995   game_em.use_random_bug =
2996     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
2997
2998   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
2999
3000   game_em.use_old_explosions            = use_old_em_engine;
3001   game_em.use_old_android               = use_old_em_engine;
3002   game_em.use_old_push_elements         = use_old_em_engine;
3003   game_em.use_old_push_into_acid        = use_old_em_engine;
3004
3005   game_em.use_wrap_around               = !use_old_em_engine;
3006
3007   // --------------------------------------------------------------------------
3008
3009   // set maximal allowed number of custom element changes per game frame
3010   game.max_num_changes_per_frame = 1;
3011
3012   // default scan direction: scan playfield from top/left to bottom/right
3013   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3014
3015   // dynamically adjust element properties according to game engine version
3016   InitElementPropertiesEngine(game.engine_version);
3017
3018   // ---------- initialize special element properties -------------------------
3019
3020   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3021   if (use_amoeba_dropping_cannot_fall_bug)
3022     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3023
3024   // ---------- initialize player's initial move delay ------------------------
3025
3026   // dynamically adjust player properties according to level information
3027   for (i = 0; i < MAX_PLAYERS; i++)
3028     game.initial_move_delay_value[i] =
3029       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3030
3031   // dynamically adjust player properties according to game engine version
3032   for (i = 0; i < MAX_PLAYERS; i++)
3033     game.initial_move_delay[i] =
3034       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3035        game.initial_move_delay_value[i] : 0);
3036
3037   // ---------- initialize player's initial push delay ------------------------
3038
3039   // dynamically adjust player properties according to game engine version
3040   game.initial_push_delay_value =
3041     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3042
3043   // ---------- initialize changing elements ----------------------------------
3044
3045   // initialize changing elements information
3046   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3047   {
3048     struct ElementInfo *ei = &element_info[i];
3049
3050     // this pointer might have been changed in the level editor
3051     ei->change = &ei->change_page[0];
3052
3053     if (!IS_CUSTOM_ELEMENT(i))
3054     {
3055       ei->change->target_element = EL_EMPTY_SPACE;
3056       ei->change->delay_fixed = 0;
3057       ei->change->delay_random = 0;
3058       ei->change->delay_frames = 1;
3059     }
3060
3061     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3062     {
3063       ei->has_change_event[j] = FALSE;
3064
3065       ei->event_page_nr[j] = 0;
3066       ei->event_page[j] = &ei->change_page[0];
3067     }
3068   }
3069
3070   // add changing elements from pre-defined list
3071   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3072   {
3073     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3074     struct ElementInfo *ei = &element_info[ch_delay->element];
3075
3076     ei->change->target_element       = ch_delay->target_element;
3077     ei->change->delay_fixed          = ch_delay->change_delay;
3078
3079     ei->change->pre_change_function  = ch_delay->pre_change_function;
3080     ei->change->change_function      = ch_delay->change_function;
3081     ei->change->post_change_function = ch_delay->post_change_function;
3082
3083     ei->change->can_change = TRUE;
3084     ei->change->can_change_or_has_action = TRUE;
3085
3086     ei->has_change_event[CE_DELAY] = TRUE;
3087
3088     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3089     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3090   }
3091
3092   // ---------- initialize internal run-time variables ------------------------
3093
3094   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3095   {
3096     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3097
3098     for (j = 0; j < ei->num_change_pages; j++)
3099     {
3100       ei->change_page[j].can_change_or_has_action =
3101         (ei->change_page[j].can_change |
3102          ei->change_page[j].has_action);
3103     }
3104   }
3105
3106   // add change events from custom element configuration
3107   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3108   {
3109     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3110
3111     for (j = 0; j < ei->num_change_pages; j++)
3112     {
3113       if (!ei->change_page[j].can_change_or_has_action)
3114         continue;
3115
3116       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3117       {
3118         // only add event page for the first page found with this event
3119         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3120         {
3121           ei->has_change_event[k] = TRUE;
3122
3123           ei->event_page_nr[k] = j;
3124           ei->event_page[k] = &ei->change_page[j];
3125         }
3126       }
3127     }
3128   }
3129
3130   // ---------- initialize reference elements in change conditions ------------
3131
3132   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3133   {
3134     int element = EL_CUSTOM_START + i;
3135     struct ElementInfo *ei = &element_info[element];
3136
3137     for (j = 0; j < ei->num_change_pages; j++)
3138     {
3139       int trigger_element = ei->change_page[j].initial_trigger_element;
3140
3141       if (trigger_element >= EL_PREV_CE_8 &&
3142           trigger_element <= EL_NEXT_CE_8)
3143         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3144
3145       ei->change_page[j].trigger_element = trigger_element;
3146     }
3147   }
3148
3149   // ---------- initialize run-time trigger player and element ----------------
3150
3151   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3152   {
3153     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3154
3155     for (j = 0; j < ei->num_change_pages; j++)
3156     {
3157       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3158       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3159       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3160       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3161       ei->change_page[j].actual_trigger_ce_value = 0;
3162       ei->change_page[j].actual_trigger_ce_score = 0;
3163     }
3164   }
3165
3166   // ---------- initialize trigger events -------------------------------------
3167
3168   // initialize trigger events information
3169   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3170     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3171       trigger_events[i][j] = FALSE;
3172
3173   // add trigger events from element change event properties
3174   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3175   {
3176     struct ElementInfo *ei = &element_info[i];
3177
3178     for (j = 0; j < ei->num_change_pages; j++)
3179     {
3180       if (!ei->change_page[j].can_change_or_has_action)
3181         continue;
3182
3183       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3184       {
3185         int trigger_element = ei->change_page[j].trigger_element;
3186
3187         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3188         {
3189           if (ei->change_page[j].has_event[k])
3190           {
3191             if (IS_GROUP_ELEMENT(trigger_element))
3192             {
3193               struct ElementGroupInfo *group =
3194                 element_info[trigger_element].group;
3195
3196               for (l = 0; l < group->num_elements_resolved; l++)
3197                 trigger_events[group->element_resolved[l]][k] = TRUE;
3198             }
3199             else if (trigger_element == EL_ANY_ELEMENT)
3200               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3201                 trigger_events[l][k] = TRUE;
3202             else
3203               trigger_events[trigger_element][k] = TRUE;
3204           }
3205         }
3206       }
3207     }
3208   }
3209
3210   // ---------- initialize push delay -----------------------------------------
3211
3212   // initialize push delay values to default
3213   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3214   {
3215     if (!IS_CUSTOM_ELEMENT(i))
3216     {
3217       // set default push delay values (corrected since version 3.0.7-1)
3218       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3219       {
3220         element_info[i].push_delay_fixed = 2;
3221         element_info[i].push_delay_random = 8;
3222       }
3223       else
3224       {
3225         element_info[i].push_delay_fixed = 8;
3226         element_info[i].push_delay_random = 8;
3227       }
3228     }
3229   }
3230
3231   // set push delay value for certain elements from pre-defined list
3232   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3233   {
3234     int e = push_delay_list[i].element;
3235
3236     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3237     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3238   }
3239
3240   // set push delay value for Supaplex elements for newer engine versions
3241   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3242   {
3243     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3244     {
3245       if (IS_SP_ELEMENT(i))
3246       {
3247         // set SP push delay to just enough to push under a falling zonk
3248         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3249
3250         element_info[i].push_delay_fixed  = delay;
3251         element_info[i].push_delay_random = 0;
3252       }
3253     }
3254   }
3255
3256   // ---------- initialize move stepsize --------------------------------------
3257
3258   // initialize move stepsize values to default
3259   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3260     if (!IS_CUSTOM_ELEMENT(i))
3261       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3262
3263   // set move stepsize value for certain elements from pre-defined list
3264   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3265   {
3266     int e = move_stepsize_list[i].element;
3267
3268     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3269
3270     // set move stepsize value for certain elements for older engine versions
3271     if (use_old_move_stepsize_for_magic_wall)
3272     {
3273       if (e == EL_MAGIC_WALL_FILLING ||
3274           e == EL_MAGIC_WALL_EMPTYING ||
3275           e == EL_BD_MAGIC_WALL_FILLING ||
3276           e == EL_BD_MAGIC_WALL_EMPTYING)
3277         element_info[e].move_stepsize *= 2;
3278     }
3279   }
3280
3281   // ---------- initialize collect score --------------------------------------
3282
3283   // initialize collect score values for custom elements from initial value
3284   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3285     if (IS_CUSTOM_ELEMENT(i))
3286       element_info[i].collect_score = element_info[i].collect_score_initial;
3287
3288   // ---------- initialize collect count --------------------------------------
3289
3290   // initialize collect count values for non-custom elements
3291   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3292     if (!IS_CUSTOM_ELEMENT(i))
3293       element_info[i].collect_count_initial = 0;
3294
3295   // add collect count values for all elements from pre-defined list
3296   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3297     element_info[collect_count_list[i].element].collect_count_initial =
3298       collect_count_list[i].count;
3299
3300   // ---------- initialize access direction -----------------------------------
3301
3302   // initialize access direction values to default (access from every side)
3303   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3304     if (!IS_CUSTOM_ELEMENT(i))
3305       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3306
3307   // set access direction value for certain elements from pre-defined list
3308   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3309     element_info[access_direction_list[i].element].access_direction =
3310       access_direction_list[i].direction;
3311
3312   // ---------- initialize explosion content ----------------------------------
3313   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3314   {
3315     if (IS_CUSTOM_ELEMENT(i))
3316       continue;
3317
3318     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3319     {
3320       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3321
3322       element_info[i].content.e[x][y] =
3323         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3324          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3325          i == EL_PLAYER_3 ? EL_EMERALD :
3326          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3327          i == EL_MOLE ? EL_EMERALD_RED :
3328          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3329          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3330          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3331          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3332          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3333          i == EL_WALL_EMERALD ? EL_EMERALD :
3334          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3335          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3336          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3337          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3338          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3339          i == EL_WALL_PEARL ? EL_PEARL :
3340          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3341          EL_EMPTY);
3342     }
3343   }
3344
3345   // ---------- initialize recursion detection --------------------------------
3346   recursion_loop_depth = 0;
3347   recursion_loop_detected = FALSE;
3348   recursion_loop_element = EL_UNDEFINED;
3349
3350   // ---------- initialize graphics engine ------------------------------------
3351   game.scroll_delay_value =
3352     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3353      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3354      !setup.forced_scroll_delay           ? 0 :
3355      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3356   game.scroll_delay_value =
3357     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3358
3359   // ---------- initialize game engine snapshots ------------------------------
3360   for (i = 0; i < MAX_PLAYERS; i++)
3361     game.snapshot.last_action[i] = 0;
3362   game.snapshot.changed_action = FALSE;
3363   game.snapshot.collected_item = FALSE;
3364   game.snapshot.mode =
3365     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3366      SNAPSHOT_MODE_EVERY_STEP :
3367      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3368      SNAPSHOT_MODE_EVERY_MOVE :
3369      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3370      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3371   game.snapshot.save_snapshot = FALSE;
3372
3373   // ---------- initialize level time for Supaplex engine ---------------------
3374   // Supaplex levels with time limit currently unsupported -- should be added
3375   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3376     level.time = 0;
3377
3378   // ---------- initialize flags for handling game actions --------------------
3379
3380   // set flags for game actions to default values
3381   game.use_key_actions = TRUE;
3382   game.use_mouse_actions = FALSE;
3383
3384   // when using Mirror Magic game engine, handle mouse events only
3385   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3386   {
3387     game.use_key_actions = FALSE;
3388     game.use_mouse_actions = TRUE;
3389   }
3390
3391   // check for custom elements with mouse click events
3392   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3393   {
3394     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3395     {
3396       int element = EL_CUSTOM_START + i;
3397
3398       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3399           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3400           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3401           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3402         game.use_mouse_actions = TRUE;
3403     }
3404   }
3405 }
3406
3407 static int get_num_special_action(int element, int action_first,
3408                                   int action_last)
3409 {
3410   int num_special_action = 0;
3411   int i, j;
3412
3413   for (i = action_first; i <= action_last; i++)
3414   {
3415     boolean found = FALSE;
3416
3417     for (j = 0; j < NUM_DIRECTIONS; j++)
3418       if (el_act_dir2img(element, i, j) !=
3419           el_act_dir2img(element, ACTION_DEFAULT, j))
3420         found = TRUE;
3421
3422     if (found)
3423       num_special_action++;
3424     else
3425       break;
3426   }
3427
3428   return num_special_action;
3429 }
3430
3431
3432 // ============================================================================
3433 // InitGame()
3434 // ----------------------------------------------------------------------------
3435 // initialize and start new game
3436 // ============================================================================
3437
3438 #if DEBUG_INIT_PLAYER
3439 static void DebugPrintPlayerStatus(char *message)
3440 {
3441   int i;
3442
3443   if (!options.debug)
3444     return;
3445
3446   Debug("game:init:player", "%s:", message);
3447
3448   for (i = 0; i < MAX_PLAYERS; i++)
3449   {
3450     struct PlayerInfo *player = &stored_player[i];
3451
3452     Debug("game:init:player",
3453           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3454           i + 1,
3455           player->present,
3456           player->connected,
3457           player->connected_locally,
3458           player->connected_network,
3459           player->active,
3460           (local_player == player ? " (local player)" : ""));
3461   }
3462 }
3463 #endif
3464
3465 void InitGame(void)
3466 {
3467   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3468   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3469   int fade_mask = REDRAW_FIELD;
3470
3471   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3472   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3473   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3474   int initial_move_dir = MV_DOWN;
3475   int i, j, x, y;
3476
3477   // required here to update video display before fading (FIX THIS)
3478   DrawMaskedBorder(REDRAW_DOOR_2);
3479
3480   if (!game.restart_level)
3481     CloseDoor(DOOR_CLOSE_1);
3482
3483   SetGameStatus(GAME_MODE_PLAYING);
3484
3485   if (level_editor_test_game)
3486     FadeSkipNextFadeOut();
3487   else
3488     FadeSetEnterScreen();
3489
3490   if (CheckFadeAll())
3491     fade_mask = REDRAW_ALL;
3492
3493   FadeLevelSoundsAndMusic();
3494
3495   ExpireSoundLoops(TRUE);
3496
3497   FadeOut(fade_mask);
3498
3499   if (level_editor_test_game)
3500     FadeSkipNextFadeIn();
3501
3502   // needed if different viewport properties defined for playing
3503   ChangeViewportPropertiesIfNeeded();
3504
3505   ClearField();
3506
3507   DrawCompleteVideoDisplay();
3508
3509   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3510
3511   InitGameEngine();
3512   InitGameControlValues();
3513
3514   // initialize tape actions from game when recording tape
3515   if (tape.recording)
3516   {
3517     tape.use_key_actions   = game.use_key_actions;
3518     tape.use_mouse_actions = game.use_mouse_actions;
3519   }
3520
3521   // don't play tapes over network
3522   network_playing = (network.enabled && !tape.playing);
3523
3524   for (i = 0; i < MAX_PLAYERS; i++)
3525   {
3526     struct PlayerInfo *player = &stored_player[i];
3527
3528     player->index_nr = i;
3529     player->index_bit = (1 << i);
3530     player->element_nr = EL_PLAYER_1 + i;
3531
3532     player->present = FALSE;
3533     player->active = FALSE;
3534     player->mapped = FALSE;
3535
3536     player->killed = FALSE;
3537     player->reanimated = FALSE;
3538     player->buried = FALSE;
3539
3540     player->action = 0;
3541     player->effective_action = 0;
3542     player->programmed_action = 0;
3543     player->snap_action = 0;
3544
3545     player->mouse_action.lx = 0;
3546     player->mouse_action.ly = 0;
3547     player->mouse_action.button = 0;
3548     player->mouse_action.button_hint = 0;
3549
3550     player->effective_mouse_action.lx = 0;
3551     player->effective_mouse_action.ly = 0;
3552     player->effective_mouse_action.button = 0;
3553     player->effective_mouse_action.button_hint = 0;
3554
3555     for (j = 0; j < MAX_NUM_KEYS; j++)
3556       player->key[j] = FALSE;
3557
3558     player->num_white_keys = 0;
3559
3560     player->dynabomb_count = 0;
3561     player->dynabomb_size = 1;
3562     player->dynabombs_left = 0;
3563     player->dynabomb_xl = FALSE;
3564
3565     player->MovDir = initial_move_dir;
3566     player->MovPos = 0;
3567     player->GfxPos = 0;
3568     player->GfxDir = initial_move_dir;
3569     player->GfxAction = ACTION_DEFAULT;
3570     player->Frame = 0;
3571     player->StepFrame = 0;
3572
3573     player->initial_element = player->element_nr;
3574     player->artwork_element =
3575       (level.use_artwork_element[i] ? level.artwork_element[i] :
3576        player->element_nr);
3577     player->use_murphy = FALSE;
3578
3579     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3580     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3581
3582     player->gravity = level.initial_player_gravity[i];
3583
3584     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3585
3586     player->actual_frame_counter = 0;
3587
3588     player->step_counter = 0;
3589
3590     player->last_move_dir = initial_move_dir;
3591
3592     player->is_active = FALSE;
3593
3594     player->is_waiting = FALSE;
3595     player->is_moving = FALSE;
3596     player->is_auto_moving = FALSE;
3597     player->is_digging = FALSE;
3598     player->is_snapping = FALSE;
3599     player->is_collecting = FALSE;
3600     player->is_pushing = FALSE;
3601     player->is_switching = FALSE;
3602     player->is_dropping = FALSE;
3603     player->is_dropping_pressed = FALSE;
3604
3605     player->is_bored = FALSE;
3606     player->is_sleeping = FALSE;
3607
3608     player->was_waiting = TRUE;
3609     player->was_moving = FALSE;
3610     player->was_snapping = FALSE;
3611     player->was_dropping = FALSE;
3612
3613     player->force_dropping = FALSE;
3614
3615     player->frame_counter_bored = -1;
3616     player->frame_counter_sleeping = -1;
3617
3618     player->anim_delay_counter = 0;
3619     player->post_delay_counter = 0;
3620
3621     player->dir_waiting = initial_move_dir;
3622     player->action_waiting = ACTION_DEFAULT;
3623     player->last_action_waiting = ACTION_DEFAULT;
3624     player->special_action_bored = ACTION_DEFAULT;
3625     player->special_action_sleeping = ACTION_DEFAULT;
3626
3627     player->switch_x = -1;
3628     player->switch_y = -1;
3629
3630     player->drop_x = -1;
3631     player->drop_y = -1;
3632
3633     player->show_envelope = 0;
3634
3635     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3636
3637     player->push_delay       = -1;      // initialized when pushing starts
3638     player->push_delay_value = game.initial_push_delay_value;
3639
3640     player->drop_delay = 0;
3641     player->drop_pressed_delay = 0;
3642
3643     player->last_jx = -1;
3644     player->last_jy = -1;
3645     player->jx = -1;
3646     player->jy = -1;
3647
3648     player->shield_normal_time_left = 0;
3649     player->shield_deadly_time_left = 0;
3650
3651     player->inventory_infinite_element = EL_UNDEFINED;
3652     player->inventory_size = 0;
3653
3654     if (level.use_initial_inventory[i])
3655     {
3656       for (j = 0; j < level.initial_inventory_size[i]; j++)
3657       {
3658         int element = level.initial_inventory_content[i][j];
3659         int collect_count = element_info[element].collect_count_initial;
3660         int k;
3661
3662         if (!IS_CUSTOM_ELEMENT(element))
3663           collect_count = 1;
3664
3665         if (collect_count == 0)
3666           player->inventory_infinite_element = element;
3667         else
3668           for (k = 0; k < collect_count; k++)
3669             if (player->inventory_size < MAX_INVENTORY_SIZE)
3670               player->inventory_element[player->inventory_size++] = element;
3671       }
3672     }
3673
3674     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3675     SnapField(player, 0, 0);
3676
3677     map_player_action[i] = i;
3678   }
3679
3680   network_player_action_received = FALSE;
3681
3682   // initial null action
3683   if (network_playing)
3684     SendToServer_MovePlayer(MV_NONE);
3685
3686   FrameCounter = 0;
3687   TimeFrames = 0;
3688   TimePlayed = 0;
3689   TimeLeft = level.time;
3690   TapeTime = 0;
3691
3692   ScreenMovDir = MV_NONE;
3693   ScreenMovPos = 0;
3694   ScreenGfxPos = 0;
3695
3696   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3697
3698   game.robot_wheel_x = -1;
3699   game.robot_wheel_y = -1;
3700
3701   game.exit_x = -1;
3702   game.exit_y = -1;
3703
3704   game.all_players_gone = FALSE;
3705
3706   game.LevelSolved = FALSE;
3707   game.GameOver = FALSE;
3708
3709   game.GamePlayed = !tape.playing;
3710
3711   game.LevelSolved_GameWon = FALSE;
3712   game.LevelSolved_GameEnd = FALSE;
3713   game.LevelSolved_SaveTape = FALSE;
3714   game.LevelSolved_SaveScore = FALSE;
3715
3716   game.LevelSolved_CountingTime = 0;
3717   game.LevelSolved_CountingScore = 0;
3718   game.LevelSolved_CountingHealth = 0;
3719
3720   game.panel.active = TRUE;
3721
3722   game.no_time_limit = (level.time == 0);
3723
3724   game.yamyam_content_nr = 0;
3725   game.robot_wheel_active = FALSE;
3726   game.magic_wall_active = FALSE;
3727   game.magic_wall_time_left = 0;
3728   game.light_time_left = 0;
3729   game.timegate_time_left = 0;
3730   game.switchgate_pos = 0;
3731   game.wind_direction = level.wind_direction_initial;
3732
3733   game.score = 0;
3734   game.score_final = 0;
3735
3736   game.health = MAX_HEALTH;
3737   game.health_final = MAX_HEALTH;
3738
3739   game.gems_still_needed = level.gems_needed;
3740   game.sokoban_fields_still_needed = 0;
3741   game.sokoban_objects_still_needed = 0;
3742   game.lights_still_needed = 0;
3743   game.players_still_needed = 0;
3744   game.friends_still_needed = 0;
3745
3746   game.lenses_time_left = 0;
3747   game.magnify_time_left = 0;
3748
3749   game.ball_active = level.ball_active_initial;
3750   game.ball_content_nr = 0;
3751
3752   game.explosions_delayed = TRUE;
3753
3754   game.envelope_active = FALSE;
3755
3756   for (i = 0; i < NUM_BELTS; i++)
3757   {
3758     game.belt_dir[i] = MV_NONE;
3759     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3760   }
3761
3762   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3763     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3764
3765 #if DEBUG_INIT_PLAYER
3766   DebugPrintPlayerStatus("Player status at level initialization");
3767 #endif
3768
3769   SCAN_PLAYFIELD(x, y)
3770   {
3771     Tile[x][y] = Last[x][y] = level.field[x][y];
3772     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3773     ChangeDelay[x][y] = 0;
3774     ChangePage[x][y] = -1;
3775     CustomValue[x][y] = 0;              // initialized in InitField()
3776     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3777     AmoebaNr[x][y] = 0;
3778     WasJustMoving[x][y] = 0;
3779     WasJustFalling[x][y] = 0;
3780     CheckCollision[x][y] = 0;
3781     CheckImpact[x][y] = 0;
3782     Stop[x][y] = FALSE;
3783     Pushed[x][y] = FALSE;
3784
3785     ChangeCount[x][y] = 0;
3786     ChangeEvent[x][y] = -1;
3787
3788     ExplodePhase[x][y] = 0;
3789     ExplodeDelay[x][y] = 0;
3790     ExplodeField[x][y] = EX_TYPE_NONE;
3791
3792     RunnerVisit[x][y] = 0;
3793     PlayerVisit[x][y] = 0;
3794
3795     GfxFrame[x][y] = 0;
3796     GfxRandom[x][y] = INIT_GFX_RANDOM();
3797     GfxElement[x][y] = EL_UNDEFINED;
3798     GfxAction[x][y] = ACTION_DEFAULT;
3799     GfxDir[x][y] = MV_NONE;
3800     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3801   }
3802
3803   SCAN_PLAYFIELD(x, y)
3804   {
3805     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3806       emulate_bd = FALSE;
3807     if (emulate_sb && !IS_SB_ELEMENT(Tile[x][y]))
3808       emulate_sb = FALSE;
3809     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3810       emulate_sp = FALSE;
3811
3812     InitField(x, y, TRUE);
3813
3814     ResetGfxAnimation(x, y);
3815   }
3816
3817   InitBeltMovement();
3818
3819   for (i = 0; i < MAX_PLAYERS; i++)
3820   {
3821     struct PlayerInfo *player = &stored_player[i];
3822
3823     // set number of special actions for bored and sleeping animation
3824     player->num_special_action_bored =
3825       get_num_special_action(player->artwork_element,
3826                              ACTION_BORING_1, ACTION_BORING_LAST);
3827     player->num_special_action_sleeping =
3828       get_num_special_action(player->artwork_element,
3829                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3830   }
3831
3832   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3833                     emulate_sb ? EMU_SOKOBAN :
3834                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3835
3836   // initialize type of slippery elements
3837   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3838   {
3839     if (!IS_CUSTOM_ELEMENT(i))
3840     {
3841       // default: elements slip down either to the left or right randomly
3842       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3843
3844       // SP style elements prefer to slip down on the left side
3845       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3846         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3847
3848       // BD style elements prefer to slip down on the left side
3849       if (game.emulation == EMU_BOULDERDASH)
3850         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3851     }
3852   }
3853
3854   // initialize explosion and ignition delay
3855   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3856   {
3857     if (!IS_CUSTOM_ELEMENT(i))
3858     {
3859       int num_phase = 8;
3860       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3861                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3862                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3863       int last_phase = (num_phase + 1) * delay;
3864       int half_phase = (num_phase / 2) * delay;
3865
3866       element_info[i].explosion_delay = last_phase - 1;
3867       element_info[i].ignition_delay = half_phase;
3868
3869       if (i == EL_BLACK_ORB)
3870         element_info[i].ignition_delay = 1;
3871     }
3872   }
3873
3874   // correct non-moving belts to start moving left
3875   for (i = 0; i < NUM_BELTS; i++)
3876     if (game.belt_dir[i] == MV_NONE)
3877       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3878
3879 #if USE_NEW_PLAYER_ASSIGNMENTS
3880   // use preferred player also in local single-player mode
3881   if (!network.enabled && !game.team_mode)
3882   {
3883     int new_index_nr = setup.network_player_nr;
3884
3885     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3886     {
3887       for (i = 0; i < MAX_PLAYERS; i++)
3888         stored_player[i].connected_locally = FALSE;
3889
3890       stored_player[new_index_nr].connected_locally = TRUE;
3891     }
3892   }
3893
3894   for (i = 0; i < MAX_PLAYERS; i++)
3895   {
3896     stored_player[i].connected = FALSE;
3897
3898     // in network game mode, the local player might not be the first player
3899     if (stored_player[i].connected_locally)
3900       local_player = &stored_player[i];
3901   }
3902
3903   if (!network.enabled)
3904     local_player->connected = TRUE;
3905
3906   if (tape.playing)
3907   {
3908     for (i = 0; i < MAX_PLAYERS; i++)
3909       stored_player[i].connected = tape.player_participates[i];
3910   }
3911   else if (network.enabled)
3912   {
3913     // add team mode players connected over the network (needed for correct
3914     // assignment of player figures from level to locally playing players)
3915
3916     for (i = 0; i < MAX_PLAYERS; i++)
3917       if (stored_player[i].connected_network)
3918         stored_player[i].connected = TRUE;
3919   }
3920   else if (game.team_mode)
3921   {
3922     // try to guess locally connected team mode players (needed for correct
3923     // assignment of player figures from level to locally playing players)
3924
3925     for (i = 0; i < MAX_PLAYERS; i++)
3926       if (setup.input[i].use_joystick ||
3927           setup.input[i].key.left != KSYM_UNDEFINED)
3928         stored_player[i].connected = TRUE;
3929   }
3930
3931 #if DEBUG_INIT_PLAYER
3932   DebugPrintPlayerStatus("Player status after level initialization");
3933 #endif
3934
3935 #if DEBUG_INIT_PLAYER
3936   Debug("game:init:player", "Reassigning players ...");
3937 #endif
3938
3939   // check if any connected player was not found in playfield
3940   for (i = 0; i < MAX_PLAYERS; i++)
3941   {
3942     struct PlayerInfo *player = &stored_player[i];
3943
3944     if (player->connected && !player->present)
3945     {
3946       struct PlayerInfo *field_player = NULL;
3947
3948 #if DEBUG_INIT_PLAYER
3949       Debug("game:init:player",
3950             "- looking for field player for player %d ...", i + 1);
3951 #endif
3952
3953       // assign first free player found that is present in the playfield
3954
3955       // first try: look for unmapped playfield player that is not connected
3956       for (j = 0; j < MAX_PLAYERS; j++)
3957         if (field_player == NULL &&
3958             stored_player[j].present &&
3959             !stored_player[j].mapped &&
3960             !stored_player[j].connected)
3961           field_player = &stored_player[j];
3962
3963       // second try: look for *any* unmapped playfield player
3964       for (j = 0; j < MAX_PLAYERS; j++)
3965         if (field_player == NULL &&
3966             stored_player[j].present &&
3967             !stored_player[j].mapped)
3968           field_player = &stored_player[j];
3969
3970       if (field_player != NULL)
3971       {
3972         int jx = field_player->jx, jy = field_player->jy;
3973
3974 #if DEBUG_INIT_PLAYER
3975         Debug("game:init:player", "- found player %d",
3976               field_player->index_nr + 1);
3977 #endif
3978
3979         player->present = FALSE;
3980         player->active = FALSE;
3981
3982         field_player->present = TRUE;
3983         field_player->active = TRUE;
3984
3985         /*
3986         player->initial_element = field_player->initial_element;
3987         player->artwork_element = field_player->artwork_element;
3988
3989         player->block_last_field       = field_player->block_last_field;
3990         player->block_delay_adjustment = field_player->block_delay_adjustment;
3991         */
3992
3993         StorePlayer[jx][jy] = field_player->element_nr;
3994
3995         field_player->jx = field_player->last_jx = jx;
3996         field_player->jy = field_player->last_jy = jy;
3997
3998         if (local_player == player)
3999           local_player = field_player;
4000
4001         map_player_action[field_player->index_nr] = i;
4002
4003         field_player->mapped = TRUE;
4004
4005 #if DEBUG_INIT_PLAYER
4006         Debug("game:init:player", "- map_player_action[%d] == %d",
4007               field_player->index_nr + 1, i + 1);
4008 #endif
4009       }
4010     }
4011
4012     if (player->connected && player->present)
4013       player->mapped = TRUE;
4014   }
4015
4016 #if DEBUG_INIT_PLAYER
4017   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4018 #endif
4019
4020 #else
4021
4022   // check if any connected player was not found in playfield
4023   for (i = 0; i < MAX_PLAYERS; i++)
4024   {
4025     struct PlayerInfo *player = &stored_player[i];
4026
4027     if (player->connected && !player->present)
4028     {
4029       for (j = 0; j < MAX_PLAYERS; j++)
4030       {
4031         struct PlayerInfo *field_player = &stored_player[j];
4032         int jx = field_player->jx, jy = field_player->jy;
4033
4034         // assign first free player found that is present in the playfield
4035         if (field_player->present && !field_player->connected)
4036         {
4037           player->present = TRUE;
4038           player->active = TRUE;
4039
4040           field_player->present = FALSE;
4041           field_player->active = FALSE;
4042
4043           player->initial_element = field_player->initial_element;
4044           player->artwork_element = field_player->artwork_element;
4045
4046           player->block_last_field       = field_player->block_last_field;
4047           player->block_delay_adjustment = field_player->block_delay_adjustment;
4048
4049           StorePlayer[jx][jy] = player->element_nr;
4050
4051           player->jx = player->last_jx = jx;
4052           player->jy = player->last_jy = jy;
4053
4054           break;
4055         }
4056       }
4057     }
4058   }
4059 #endif
4060
4061 #if 0
4062   Debug("game:init:player", "local_player->present == %d",
4063         local_player->present);
4064 #endif
4065
4066   // set focus to local player for network games, else to all players
4067   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4068   game.centered_player_nr_next = game.centered_player_nr;
4069   game.set_centered_player = FALSE;
4070   game.set_centered_player_wrap = FALSE;
4071
4072   if (network_playing && tape.recording)
4073   {
4074     // store client dependent player focus when recording network games
4075     tape.centered_player_nr_next = game.centered_player_nr_next;
4076     tape.set_centered_player = TRUE;
4077   }
4078
4079   if (tape.playing)
4080   {
4081     // when playing a tape, eliminate all players who do not participate
4082
4083 #if USE_NEW_PLAYER_ASSIGNMENTS
4084
4085     if (!game.team_mode)
4086     {
4087       for (i = 0; i < MAX_PLAYERS; i++)
4088       {
4089         if (stored_player[i].active &&
4090             !tape.player_participates[map_player_action[i]])
4091         {
4092           struct PlayerInfo *player = &stored_player[i];
4093           int jx = player->jx, jy = player->jy;
4094
4095 #if DEBUG_INIT_PLAYER
4096           Debug("game:init:player", "Removing player %d at (%d, %d)",
4097                 i + 1, jx, jy);
4098 #endif
4099
4100           player->active = FALSE;
4101           StorePlayer[jx][jy] = 0;
4102           Tile[jx][jy] = EL_EMPTY;
4103         }
4104       }
4105     }
4106
4107 #else
4108
4109     for (i = 0; i < MAX_PLAYERS; i++)
4110     {
4111       if (stored_player[i].active &&
4112           !tape.player_participates[i])
4113       {
4114         struct PlayerInfo *player = &stored_player[i];
4115         int jx = player->jx, jy = player->jy;
4116
4117         player->active = FALSE;
4118         StorePlayer[jx][jy] = 0;
4119         Tile[jx][jy] = EL_EMPTY;
4120       }
4121     }
4122 #endif
4123   }
4124   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4125   {
4126     // when in single player mode, eliminate all but the local player
4127
4128     for (i = 0; i < MAX_PLAYERS; i++)
4129     {
4130       struct PlayerInfo *player = &stored_player[i];
4131
4132       if (player->active && player != local_player)
4133       {
4134         int jx = player->jx, jy = player->jy;
4135
4136         player->active = FALSE;
4137         player->present = FALSE;
4138
4139         StorePlayer[jx][jy] = 0;
4140         Tile[jx][jy] = EL_EMPTY;
4141       }
4142     }
4143   }
4144
4145   for (i = 0; i < MAX_PLAYERS; i++)
4146     if (stored_player[i].active)
4147       game.players_still_needed++;
4148
4149   if (level.solved_by_one_player)
4150     game.players_still_needed = 1;
4151
4152   // when recording the game, store which players take part in the game
4153   if (tape.recording)
4154   {
4155 #if USE_NEW_PLAYER_ASSIGNMENTS
4156     for (i = 0; i < MAX_PLAYERS; i++)
4157       if (stored_player[i].connected)
4158         tape.player_participates[i] = TRUE;
4159 #else
4160     for (i = 0; i < MAX_PLAYERS; i++)
4161       if (stored_player[i].active)
4162         tape.player_participates[i] = TRUE;
4163 #endif
4164   }
4165
4166 #if DEBUG_INIT_PLAYER
4167   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4168 #endif
4169
4170   if (BorderElement == EL_EMPTY)
4171   {
4172     SBX_Left = 0;
4173     SBX_Right = lev_fieldx - SCR_FIELDX;
4174     SBY_Upper = 0;
4175     SBY_Lower = lev_fieldy - SCR_FIELDY;
4176   }
4177   else
4178   {
4179     SBX_Left = -1;
4180     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4181     SBY_Upper = -1;
4182     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4183   }
4184
4185   if (full_lev_fieldx <= SCR_FIELDX)
4186     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4187   if (full_lev_fieldy <= SCR_FIELDY)
4188     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4189
4190   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4191     SBX_Left--;
4192   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4193     SBY_Upper--;
4194
4195   // if local player not found, look for custom element that might create
4196   // the player (make some assumptions about the right custom element)
4197   if (!local_player->present)
4198   {
4199     int start_x = 0, start_y = 0;
4200     int found_rating = 0;
4201     int found_element = EL_UNDEFINED;
4202     int player_nr = local_player->index_nr;
4203
4204     SCAN_PLAYFIELD(x, y)
4205     {
4206       int element = Tile[x][y];
4207       int content;
4208       int xx, yy;
4209       boolean is_player;
4210
4211       if (level.use_start_element[player_nr] &&
4212           level.start_element[player_nr] == element &&
4213           found_rating < 4)
4214       {
4215         start_x = x;
4216         start_y = y;
4217
4218         found_rating = 4;
4219         found_element = element;
4220       }
4221
4222       if (!IS_CUSTOM_ELEMENT(element))
4223         continue;
4224
4225       if (CAN_CHANGE(element))
4226       {
4227         for (i = 0; i < element_info[element].num_change_pages; i++)
4228         {
4229           // check for player created from custom element as single target
4230           content = element_info[element].change_page[i].target_element;
4231           is_player = ELEM_IS_PLAYER(content);
4232
4233           if (is_player && (found_rating < 3 ||
4234                             (found_rating == 3 && element < found_element)))
4235           {
4236             start_x = x;
4237             start_y = y;
4238
4239             found_rating = 3;
4240             found_element = element;
4241           }
4242         }
4243       }
4244
4245       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4246       {
4247         // check for player created from custom element as explosion content
4248         content = element_info[element].content.e[xx][yy];
4249         is_player = ELEM_IS_PLAYER(content);
4250
4251         if (is_player && (found_rating < 2 ||
4252                           (found_rating == 2 && element < found_element)))
4253         {
4254           start_x = x + xx - 1;
4255           start_y = y + yy - 1;
4256
4257           found_rating = 2;
4258           found_element = element;
4259         }
4260
4261         if (!CAN_CHANGE(element))
4262           continue;
4263
4264         for (i = 0; i < element_info[element].num_change_pages; i++)
4265         {
4266           // check for player created from custom element as extended target
4267           content =
4268             element_info[element].change_page[i].target_content.e[xx][yy];
4269
4270           is_player = ELEM_IS_PLAYER(content);
4271
4272           if (is_player && (found_rating < 1 ||
4273                             (found_rating == 1 && element < found_element)))
4274           {
4275             start_x = x + xx - 1;
4276             start_y = y + yy - 1;
4277
4278             found_rating = 1;
4279             found_element = element;
4280           }
4281         }
4282       }
4283     }
4284
4285     scroll_x = SCROLL_POSITION_X(start_x);
4286     scroll_y = SCROLL_POSITION_Y(start_y);
4287   }
4288   else
4289   {
4290     scroll_x = SCROLL_POSITION_X(local_player->jx);
4291     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4292   }
4293
4294   // !!! FIX THIS (START) !!!
4295   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4296   {
4297     InitGameEngine_EM();
4298   }
4299   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4300   {
4301     InitGameEngine_SP();
4302   }
4303   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4304   {
4305     InitGameEngine_MM();
4306   }
4307   else
4308   {
4309     DrawLevel(REDRAW_FIELD);
4310     DrawAllPlayers();
4311
4312     // after drawing the level, correct some elements
4313     if (game.timegate_time_left == 0)
4314       CloseAllOpenTimegates();
4315   }
4316
4317   // blit playfield from scroll buffer to normal back buffer for fading in
4318   BlitScreenToBitmap(backbuffer);
4319   // !!! FIX THIS (END) !!!
4320
4321   DrawMaskedBorder(fade_mask);
4322
4323   FadeIn(fade_mask);
4324
4325 #if 1
4326   // full screen redraw is required at this point in the following cases:
4327   // - special editor door undrawn when game was started from level editor
4328   // - drawing area (playfield) was changed and has to be removed completely
4329   redraw_mask = REDRAW_ALL;
4330   BackToFront();
4331 #endif
4332
4333   if (!game.restart_level)
4334   {
4335     // copy default game door content to main double buffer
4336
4337     // !!! CHECK AGAIN !!!
4338     SetPanelBackground();
4339     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4340     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4341   }
4342
4343   SetPanelBackground();
4344   SetDrawBackgroundMask(REDRAW_DOOR_1);
4345
4346   UpdateAndDisplayGameControlValues();
4347
4348   if (!game.restart_level)
4349   {
4350     UnmapGameButtons();
4351     UnmapTapeButtons();
4352
4353     FreeGameButtons();
4354     CreateGameButtons();
4355
4356     MapGameButtons();
4357     MapTapeButtons();
4358
4359     // copy actual game door content to door double buffer for OpenDoor()
4360     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4361
4362     OpenDoor(DOOR_OPEN_ALL);
4363
4364     KeyboardAutoRepeatOffUnlessAutoplay();
4365
4366 #if DEBUG_INIT_PLAYER
4367     DebugPrintPlayerStatus("Player status (final)");
4368 #endif
4369   }
4370
4371   UnmapAllGadgets();
4372
4373   MapGameButtons();
4374   MapTapeButtons();
4375
4376   if (!game.restart_level && !tape.playing)
4377   {
4378     LevelStats_incPlayed(level_nr);
4379
4380     SaveLevelSetup_SeriesInfo();
4381   }
4382
4383   game.restart_level = FALSE;
4384   game.restart_game_message = NULL;
4385   game.request_active = FALSE;
4386
4387   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4388     InitGameActions_MM();
4389
4390   SaveEngineSnapshotToListInitial();
4391
4392   if (!game.restart_level)
4393   {
4394     PlaySound(SND_GAME_STARTING);
4395
4396     if (setup.sound_music)
4397       PlayLevelMusic();
4398   }
4399 }
4400
4401 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4402                         int actual_player_x, int actual_player_y)
4403 {
4404   // this is used for non-R'n'D game engines to update certain engine values
4405
4406   // needed to determine if sounds are played within the visible screen area
4407   scroll_x = actual_scroll_x;
4408   scroll_y = actual_scroll_y;
4409
4410   // needed to get player position for "follow finger" playing input method
4411   local_player->jx = actual_player_x;
4412   local_player->jy = actual_player_y;
4413 }
4414
4415 void InitMovDir(int x, int y)
4416 {
4417   int i, element = Tile[x][y];
4418   static int xy[4][2] =
4419   {
4420     {  0, +1 },
4421     { +1,  0 },
4422     {  0, -1 },
4423     { -1,  0 }
4424   };
4425   static int direction[3][4] =
4426   {
4427     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4428     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4429     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4430   };
4431
4432   switch (element)
4433   {
4434     case EL_BUG_RIGHT:
4435     case EL_BUG_UP:
4436     case EL_BUG_LEFT:
4437     case EL_BUG_DOWN:
4438       Tile[x][y] = EL_BUG;
4439       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4440       break;
4441
4442     case EL_SPACESHIP_RIGHT:
4443     case EL_SPACESHIP_UP:
4444     case EL_SPACESHIP_LEFT:
4445     case EL_SPACESHIP_DOWN:
4446       Tile[x][y] = EL_SPACESHIP;
4447       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4448       break;
4449
4450     case EL_BD_BUTTERFLY_RIGHT:
4451     case EL_BD_BUTTERFLY_UP:
4452     case EL_BD_BUTTERFLY_LEFT:
4453     case EL_BD_BUTTERFLY_DOWN:
4454       Tile[x][y] = EL_BD_BUTTERFLY;
4455       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4456       break;
4457
4458     case EL_BD_FIREFLY_RIGHT:
4459     case EL_BD_FIREFLY_UP:
4460     case EL_BD_FIREFLY_LEFT:
4461     case EL_BD_FIREFLY_DOWN:
4462       Tile[x][y] = EL_BD_FIREFLY;
4463       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4464       break;
4465
4466     case EL_PACMAN_RIGHT:
4467     case EL_PACMAN_UP:
4468     case EL_PACMAN_LEFT:
4469     case EL_PACMAN_DOWN:
4470       Tile[x][y] = EL_PACMAN;
4471       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4472       break;
4473
4474     case EL_YAMYAM_LEFT:
4475     case EL_YAMYAM_RIGHT:
4476     case EL_YAMYAM_UP:
4477     case EL_YAMYAM_DOWN:
4478       Tile[x][y] = EL_YAMYAM;
4479       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4480       break;
4481
4482     case EL_SP_SNIKSNAK:
4483       MovDir[x][y] = MV_UP;
4484       break;
4485
4486     case EL_SP_ELECTRON:
4487       MovDir[x][y] = MV_LEFT;
4488       break;
4489
4490     case EL_MOLE_LEFT:
4491     case EL_MOLE_RIGHT:
4492     case EL_MOLE_UP:
4493     case EL_MOLE_DOWN:
4494       Tile[x][y] = EL_MOLE;
4495       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4496       break;
4497
4498     case EL_SPRING_LEFT:
4499     case EL_SPRING_RIGHT:
4500       Tile[x][y] = EL_SPRING;
4501       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4502       break;
4503
4504     default:
4505       if (IS_CUSTOM_ELEMENT(element))
4506       {
4507         struct ElementInfo *ei = &element_info[element];
4508         int move_direction_initial = ei->move_direction_initial;
4509         int move_pattern = ei->move_pattern;
4510
4511         if (move_direction_initial == MV_START_PREVIOUS)
4512         {
4513           if (MovDir[x][y] != MV_NONE)
4514             return;
4515
4516           move_direction_initial = MV_START_AUTOMATIC;
4517         }
4518
4519         if (move_direction_initial == MV_START_RANDOM)
4520           MovDir[x][y] = 1 << RND(4);
4521         else if (move_direction_initial & MV_ANY_DIRECTION)
4522           MovDir[x][y] = move_direction_initial;
4523         else if (move_pattern == MV_ALL_DIRECTIONS ||
4524                  move_pattern == MV_TURNING_LEFT ||
4525                  move_pattern == MV_TURNING_RIGHT ||
4526                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4527                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4528                  move_pattern == MV_TURNING_RANDOM)
4529           MovDir[x][y] = 1 << RND(4);
4530         else if (move_pattern == MV_HORIZONTAL)
4531           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4532         else if (move_pattern == MV_VERTICAL)
4533           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4534         else if (move_pattern & MV_ANY_DIRECTION)
4535           MovDir[x][y] = element_info[element].move_pattern;
4536         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4537                  move_pattern == MV_ALONG_RIGHT_SIDE)
4538         {
4539           // use random direction as default start direction
4540           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4541             MovDir[x][y] = 1 << RND(4);
4542
4543           for (i = 0; i < NUM_DIRECTIONS; i++)
4544           {
4545             int x1 = x + xy[i][0];
4546             int y1 = y + xy[i][1];
4547
4548             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4549             {
4550               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4551                 MovDir[x][y] = direction[0][i];
4552               else
4553                 MovDir[x][y] = direction[1][i];
4554
4555               break;
4556             }
4557           }
4558         }                
4559       }
4560       else
4561       {
4562         MovDir[x][y] = 1 << RND(4);
4563
4564         if (element != EL_BUG &&
4565             element != EL_SPACESHIP &&
4566             element != EL_BD_BUTTERFLY &&
4567             element != EL_BD_FIREFLY)
4568           break;
4569
4570         for (i = 0; i < NUM_DIRECTIONS; i++)
4571         {
4572           int x1 = x + xy[i][0];
4573           int y1 = y + xy[i][1];
4574
4575           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4576           {
4577             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4578             {
4579               MovDir[x][y] = direction[0][i];
4580               break;
4581             }
4582             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4583                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4584             {
4585               MovDir[x][y] = direction[1][i];
4586               break;
4587             }
4588           }
4589         }
4590       }
4591       break;
4592   }
4593
4594   GfxDir[x][y] = MovDir[x][y];
4595 }
4596
4597 void InitAmoebaNr(int x, int y)
4598 {
4599   int i;
4600   int group_nr = AmoebaNeighbourNr(x, y);
4601
4602   if (group_nr == 0)
4603   {
4604     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4605     {
4606       if (AmoebaCnt[i] == 0)
4607       {
4608         group_nr = i;
4609         break;
4610       }
4611     }
4612   }
4613
4614   AmoebaNr[x][y] = group_nr;
4615   AmoebaCnt[group_nr]++;
4616   AmoebaCnt2[group_nr]++;
4617 }
4618
4619 static void LevelSolved(void)
4620 {
4621   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4622       game.players_still_needed > 0)
4623     return;
4624
4625   game.LevelSolved = TRUE;
4626   game.GameOver = TRUE;
4627
4628   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4629                       game_em.lev->score :
4630                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4631                       game_mm.score :
4632                       game.score);
4633   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4634                        MM_HEALTH(game_mm.laser_overload_value) :
4635                        game.health);
4636
4637   game.LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed : TimeLeft);
4638   game.LevelSolved_CountingScore = game.score_final;
4639   game.LevelSolved_CountingHealth = game.health_final;
4640 }
4641
4642 void GameWon(void)
4643 {
4644   static int time_count_steps;
4645   static int time, time_final;
4646   static int score, score_final;
4647   static int health, health_final;
4648   static int game_over_delay_1 = 0;
4649   static int game_over_delay_2 = 0;
4650   static int game_over_delay_3 = 0;
4651   int game_over_delay_value_1 = 50;
4652   int game_over_delay_value_2 = 25;
4653   int game_over_delay_value_3 = 50;
4654
4655   if (!game.LevelSolved_GameWon)
4656   {
4657     int i;
4658
4659     // do not start end game actions before the player stops moving (to exit)
4660     if (local_player->active && local_player->MovPos)
4661       return;
4662
4663     game.LevelSolved_GameWon = TRUE;
4664     game.LevelSolved_SaveTape = tape.recording;
4665     game.LevelSolved_SaveScore = !tape.playing;
4666
4667     if (!tape.playing)
4668     {
4669       LevelStats_incSolved(level_nr);
4670
4671       SaveLevelSetup_SeriesInfo();
4672     }
4673
4674     if (tape.auto_play)         // tape might already be stopped here
4675       tape.auto_play_level_solved = TRUE;
4676
4677     TapeStop();
4678
4679     game_over_delay_1 = 0;
4680     game_over_delay_2 = 0;
4681     game_over_delay_3 = game_over_delay_value_3;
4682
4683     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4684     score = score_final = game.score_final;
4685     health = health_final = game.health_final;
4686
4687     if (level.score[SC_TIME_BONUS] > 0)
4688     {
4689       if (TimeLeft > 0)
4690       {
4691         time_final = 0;
4692         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4693       }
4694       else if (game.no_time_limit && TimePlayed < 999)
4695       {
4696         time_final = 999;
4697         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4698       }
4699
4700       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4701
4702       game_over_delay_1 = game_over_delay_value_1;
4703
4704       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4705       {
4706         health_final = 0;
4707         score_final += health * level.score[SC_TIME_BONUS];
4708
4709         game_over_delay_2 = game_over_delay_value_2;
4710       }
4711
4712       game.score_final = score_final;
4713       game.health_final = health_final;
4714     }
4715
4716     if (level_editor_test_game)
4717     {
4718       time = time_final;
4719       score = score_final;
4720
4721       game.LevelSolved_CountingTime = time;
4722       game.LevelSolved_CountingScore = score;
4723
4724       game_panel_controls[GAME_PANEL_TIME].value = time;
4725       game_panel_controls[GAME_PANEL_SCORE].value = score;
4726
4727       DisplayGameControlValues();
4728     }
4729
4730     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4731     {
4732       // check if last player has left the level
4733       if (game.exit_x >= 0 &&
4734           game.exit_y >= 0)
4735       {
4736         int x = game.exit_x;
4737         int y = game.exit_y;
4738         int element = Tile[x][y];
4739
4740         // close exit door after last player
4741         if ((game.all_players_gone &&
4742              (element == EL_EXIT_OPEN ||
4743               element == EL_SP_EXIT_OPEN ||
4744               element == EL_STEEL_EXIT_OPEN)) ||
4745             element == EL_EM_EXIT_OPEN ||
4746             element == EL_EM_STEEL_EXIT_OPEN)
4747         {
4748
4749           Tile[x][y] =
4750             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4751              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4752              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4753              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4754              EL_EM_STEEL_EXIT_CLOSING);
4755
4756           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4757         }
4758
4759         // player disappears
4760         DrawLevelField(x, y);
4761       }
4762
4763       for (i = 0; i < MAX_PLAYERS; i++)
4764       {
4765         struct PlayerInfo *player = &stored_player[i];
4766
4767         if (player->present)
4768         {
4769           RemovePlayer(player);
4770
4771           // player disappears
4772           DrawLevelField(player->jx, player->jy);
4773         }
4774       }
4775     }
4776
4777     PlaySound(SND_GAME_WINNING);
4778   }
4779
4780   if (game_over_delay_1 > 0)
4781   {
4782     game_over_delay_1--;
4783
4784     return;
4785   }
4786
4787   if (time != time_final)
4788   {
4789     int time_to_go = ABS(time_final - time);
4790     int time_count_dir = (time < time_final ? +1 : -1);
4791
4792     if (time_to_go < time_count_steps)
4793       time_count_steps = 1;
4794
4795     time  += time_count_steps * time_count_dir;
4796     score += time_count_steps * level.score[SC_TIME_BONUS];
4797
4798     game.LevelSolved_CountingTime = time;
4799     game.LevelSolved_CountingScore = score;
4800
4801     game_panel_controls[GAME_PANEL_TIME].value = time;
4802     game_panel_controls[GAME_PANEL_SCORE].value = score;
4803
4804     DisplayGameControlValues();
4805
4806     if (time == time_final)
4807       StopSound(SND_GAME_LEVELTIME_BONUS);
4808     else if (setup.sound_loops)
4809       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4810     else
4811       PlaySound(SND_GAME_LEVELTIME_BONUS);
4812
4813     return;
4814   }
4815
4816   if (game_over_delay_2 > 0)
4817   {
4818     game_over_delay_2--;
4819
4820     return;
4821   }
4822
4823   if (health != health_final)
4824   {
4825     int health_count_dir = (health < health_final ? +1 : -1);
4826
4827     health += health_count_dir;
4828     score  += level.score[SC_TIME_BONUS];
4829
4830     game.LevelSolved_CountingHealth = health;
4831     game.LevelSolved_CountingScore = score;
4832
4833     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4834     game_panel_controls[GAME_PANEL_SCORE].value = score;
4835
4836     DisplayGameControlValues();
4837
4838     if (health == health_final)
4839       StopSound(SND_GAME_LEVELTIME_BONUS);
4840     else if (setup.sound_loops)
4841       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4842     else
4843       PlaySound(SND_GAME_LEVELTIME_BONUS);
4844
4845     return;
4846   }
4847
4848   game.panel.active = FALSE;
4849
4850   if (game_over_delay_3 > 0)
4851   {
4852     game_over_delay_3--;
4853
4854     return;
4855   }
4856
4857   GameEnd();
4858 }
4859
4860 void GameEnd(void)
4861 {
4862   // used instead of "level_nr" (needed for network games)
4863   int last_level_nr = levelset.level_nr;
4864   int hi_pos;
4865
4866   game.LevelSolved_GameEnd = TRUE;
4867
4868   if (game.LevelSolved_SaveTape)
4869   {
4870     // make sure that request dialog to save tape does not open door again
4871     if (!global.use_envelope_request)
4872       CloseDoor(DOOR_CLOSE_1);
4873
4874     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4875   }
4876
4877   // if no tape is to be saved, close both doors simultaneously
4878   CloseDoor(DOOR_CLOSE_ALL);
4879
4880   if (level_editor_test_game)
4881   {
4882     SetGameStatus(GAME_MODE_MAIN);
4883
4884     DrawMainMenu();
4885
4886     return;
4887   }
4888
4889   if (!game.LevelSolved_SaveScore)
4890   {
4891     SetGameStatus(GAME_MODE_MAIN);
4892
4893     DrawMainMenu();
4894
4895     return;
4896   }
4897
4898   if (level_nr == leveldir_current->handicap_level)
4899   {
4900     leveldir_current->handicap_level++;
4901
4902     SaveLevelSetup_SeriesInfo();
4903   }
4904
4905   if (setup.increment_levels &&
4906       level_nr < leveldir_current->last_level &&
4907       !network_playing)
4908   {
4909     level_nr++;         // advance to next level
4910     TapeErase();        // start with empty tape
4911
4912     if (setup.auto_play_next_level)
4913     {
4914       LoadLevel(level_nr);
4915
4916       SaveLevelSetup_SeriesInfo();
4917     }
4918   }
4919
4920   hi_pos = NewHiScore(last_level_nr);
4921
4922   if (hi_pos >= 0 && !setup.skip_scores_after_game)
4923   {
4924     SetGameStatus(GAME_MODE_SCORES);
4925
4926     DrawHallOfFame(last_level_nr, hi_pos);
4927   }
4928   else if (setup.auto_play_next_level && setup.increment_levels &&
4929            last_level_nr < leveldir_current->last_level &&
4930            !network_playing)
4931   {
4932     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4933   }
4934   else
4935   {
4936     SetGameStatus(GAME_MODE_MAIN);
4937
4938     DrawMainMenu();
4939   }
4940 }
4941
4942 int NewHiScore(int level_nr)
4943 {
4944   int k, l;
4945   int position = -1;
4946   boolean one_score_entry_per_name = !program.many_scores_per_name;
4947
4948   LoadScore(level_nr);
4949
4950   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4951       game.score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4952     return -1;
4953
4954   for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4955   {
4956     if (game.score_final > highscore[k].Score)
4957     {
4958       // player has made it to the hall of fame
4959
4960       if (k < MAX_SCORE_ENTRIES - 1)
4961       {
4962         int m = MAX_SCORE_ENTRIES - 1;
4963
4964         if (one_score_entry_per_name)
4965         {
4966           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4967             if (strEqual(setup.player_name, highscore[l].Name))
4968               m = l;
4969
4970           if (m == k)   // player's new highscore overwrites his old one
4971             goto put_into_list;
4972         }
4973
4974         for (l = m; l > k; l--)
4975         {
4976           strcpy(highscore[l].Name, highscore[l - 1].Name);
4977           highscore[l].Score = highscore[l - 1].Score;
4978         }
4979       }
4980
4981       put_into_list:
4982
4983       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4984       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4985       highscore[k].Score = game.score_final;
4986       position = k;
4987
4988       break;
4989     }
4990     else if (one_score_entry_per_name &&
4991              !strncmp(setup.player_name, highscore[k].Name,
4992                       MAX_PLAYER_NAME_LEN))
4993       break;    // player already there with a higher score
4994   }
4995
4996   if (position >= 0) 
4997     SaveScore(level_nr);
4998
4999   return position;
5000 }
5001
5002 static int getElementMoveStepsizeExt(int x, int y, int direction)
5003 {
5004   int element = Tile[x][y];
5005   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5006   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5007   int horiz_move = (dx != 0);
5008   int sign = (horiz_move ? dx : dy);
5009   int step = sign * element_info[element].move_stepsize;
5010
5011   // special values for move stepsize for spring and things on conveyor belt
5012   if (horiz_move)
5013   {
5014     if (CAN_FALL(element) &&
5015         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5016       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5017     else if (element == EL_SPRING)
5018       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5019   }
5020
5021   return step;
5022 }
5023
5024 static int getElementMoveStepsize(int x, int y)
5025 {
5026   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5027 }
5028
5029 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5030 {
5031   if (player->GfxAction != action || player->GfxDir != dir)
5032   {
5033     player->GfxAction = action;
5034     player->GfxDir = dir;
5035     player->Frame = 0;
5036     player->StepFrame = 0;
5037   }
5038 }
5039
5040 static void ResetGfxFrame(int x, int y)
5041 {
5042   // profiling showed that "autotest" spends 10~20% of its time in this function
5043   if (DrawingDeactivatedField())
5044     return;
5045
5046   int element = Tile[x][y];
5047   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5048
5049   if (graphic_info[graphic].anim_global_sync)
5050     GfxFrame[x][y] = FrameCounter;
5051   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5052     GfxFrame[x][y] = CustomValue[x][y];
5053   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5054     GfxFrame[x][y] = element_info[element].collect_score;
5055   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5056     GfxFrame[x][y] = ChangeDelay[x][y];
5057 }
5058
5059 static void ResetGfxAnimation(int x, int y)
5060 {
5061   GfxAction[x][y] = ACTION_DEFAULT;
5062   GfxDir[x][y] = MovDir[x][y];
5063   GfxFrame[x][y] = 0;
5064
5065   ResetGfxFrame(x, y);
5066 }
5067
5068 static void ResetRandomAnimationValue(int x, int y)
5069 {
5070   GfxRandom[x][y] = INIT_GFX_RANDOM();
5071 }
5072
5073 static void InitMovingField(int x, int y, int direction)
5074 {
5075   int element = Tile[x][y];
5076   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5077   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5078   int newx = x + dx;
5079   int newy = y + dy;
5080   boolean is_moving_before, is_moving_after;
5081
5082   // check if element was/is moving or being moved before/after mode change
5083   is_moving_before = (WasJustMoving[x][y] != 0);
5084   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5085
5086   // reset animation only for moving elements which change direction of moving
5087   // or which just started or stopped moving
5088   // (else CEs with property "can move" / "not moving" are reset each frame)
5089   if (is_moving_before != is_moving_after ||
5090       direction != MovDir[x][y])
5091     ResetGfxAnimation(x, y);
5092
5093   MovDir[x][y] = direction;
5094   GfxDir[x][y] = direction;
5095
5096   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5097                      direction == MV_DOWN && CAN_FALL(element) ?
5098                      ACTION_FALLING : ACTION_MOVING);
5099
5100   // this is needed for CEs with property "can move" / "not moving"
5101
5102   if (is_moving_after)
5103   {
5104     if (Tile[newx][newy] == EL_EMPTY)
5105       Tile[newx][newy] = EL_BLOCKED;
5106
5107     MovDir[newx][newy] = MovDir[x][y];
5108
5109     CustomValue[newx][newy] = CustomValue[x][y];
5110
5111     GfxFrame[newx][newy] = GfxFrame[x][y];
5112     GfxRandom[newx][newy] = GfxRandom[x][y];
5113     GfxAction[newx][newy] = GfxAction[x][y];
5114     GfxDir[newx][newy] = GfxDir[x][y];
5115   }
5116 }
5117
5118 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5119 {
5120   int direction = MovDir[x][y];
5121   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5122   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5123
5124   *goes_to_x = newx;
5125   *goes_to_y = newy;
5126 }
5127
5128 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5129 {
5130   int oldx = x, oldy = y;
5131   int direction = MovDir[x][y];
5132
5133   if (direction == MV_LEFT)
5134     oldx++;
5135   else if (direction == MV_RIGHT)
5136     oldx--;
5137   else if (direction == MV_UP)
5138     oldy++;
5139   else if (direction == MV_DOWN)
5140     oldy--;
5141
5142   *comes_from_x = oldx;
5143   *comes_from_y = oldy;
5144 }
5145
5146 static int MovingOrBlocked2Element(int x, int y)
5147 {
5148   int element = Tile[x][y];
5149
5150   if (element == EL_BLOCKED)
5151   {
5152     int oldx, oldy;
5153
5154     Blocked2Moving(x, y, &oldx, &oldy);
5155     return Tile[oldx][oldy];
5156   }
5157   else
5158     return element;
5159 }
5160
5161 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5162 {
5163   // like MovingOrBlocked2Element(), but if element is moving
5164   // and (x,y) is the field the moving element is just leaving,
5165   // return EL_BLOCKED instead of the element value
5166   int element = Tile[x][y];
5167
5168   if (IS_MOVING(x, y))
5169   {
5170     if (element == EL_BLOCKED)
5171     {
5172       int oldx, oldy;
5173
5174       Blocked2Moving(x, y, &oldx, &oldy);
5175       return Tile[oldx][oldy];
5176     }
5177     else
5178       return EL_BLOCKED;
5179   }
5180   else
5181     return element;
5182 }
5183
5184 static void RemoveField(int x, int y)
5185 {
5186   Tile[x][y] = EL_EMPTY;
5187
5188   MovPos[x][y] = 0;
5189   MovDir[x][y] = 0;
5190   MovDelay[x][y] = 0;
5191
5192   CustomValue[x][y] = 0;
5193
5194   AmoebaNr[x][y] = 0;
5195   ChangeDelay[x][y] = 0;
5196   ChangePage[x][y] = -1;
5197   Pushed[x][y] = FALSE;
5198
5199   GfxElement[x][y] = EL_UNDEFINED;
5200   GfxAction[x][y] = ACTION_DEFAULT;
5201   GfxDir[x][y] = MV_NONE;
5202 }
5203
5204 static void RemoveMovingField(int x, int y)
5205 {
5206   int oldx = x, oldy = y, newx = x, newy = y;
5207   int element = Tile[x][y];
5208   int next_element = EL_UNDEFINED;
5209
5210   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5211     return;
5212
5213   if (IS_MOVING(x, y))
5214   {
5215     Moving2Blocked(x, y, &newx, &newy);
5216
5217     if (Tile[newx][newy] != EL_BLOCKED)
5218     {
5219       // element is moving, but target field is not free (blocked), but
5220       // already occupied by something different (example: acid pool);
5221       // in this case, only remove the moving field, but not the target
5222
5223       RemoveField(oldx, oldy);
5224
5225       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5226
5227       TEST_DrawLevelField(oldx, oldy);
5228
5229       return;
5230     }
5231   }
5232   else if (element == EL_BLOCKED)
5233   {
5234     Blocked2Moving(x, y, &oldx, &oldy);
5235     if (!IS_MOVING(oldx, oldy))
5236       return;
5237   }
5238
5239   if (element == EL_BLOCKED &&
5240       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5241        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5242        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5243        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5244        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5245        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5246     next_element = get_next_element(Tile[oldx][oldy]);
5247
5248   RemoveField(oldx, oldy);
5249   RemoveField(newx, newy);
5250
5251   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5252
5253   if (next_element != EL_UNDEFINED)
5254     Tile[oldx][oldy] = next_element;
5255
5256   TEST_DrawLevelField(oldx, oldy);
5257   TEST_DrawLevelField(newx, newy);
5258 }
5259
5260 void DrawDynamite(int x, int y)
5261 {
5262   int sx = SCREENX(x), sy = SCREENY(y);
5263   int graphic = el2img(Tile[x][y]);
5264   int frame;
5265
5266   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5267     return;
5268
5269   if (IS_WALKABLE_INSIDE(Back[x][y]))
5270     return;
5271
5272   if (Back[x][y])
5273     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5274   else if (Store[x][y])
5275     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5276
5277   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5278
5279   if (Back[x][y] || Store[x][y])
5280     DrawGraphicThruMask(sx, sy, graphic, frame);
5281   else
5282     DrawGraphic(sx, sy, graphic, frame);
5283 }
5284
5285 static void CheckDynamite(int x, int y)
5286 {
5287   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5288   {
5289     MovDelay[x][y]--;
5290
5291     if (MovDelay[x][y] != 0)
5292     {
5293       DrawDynamite(x, y);
5294       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5295
5296       return;
5297     }
5298   }
5299
5300   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5301
5302   Bang(x, y);
5303 }
5304
5305 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5306 {
5307   boolean num_checked_players = 0;
5308   int i;
5309
5310   for (i = 0; i < MAX_PLAYERS; i++)
5311   {
5312     if (stored_player[i].active)
5313     {
5314       int sx = stored_player[i].jx;
5315       int sy = stored_player[i].jy;
5316
5317       if (num_checked_players == 0)
5318       {
5319         *sx1 = *sx2 = sx;
5320         *sy1 = *sy2 = sy;
5321       }
5322       else
5323       {
5324         *sx1 = MIN(*sx1, sx);
5325         *sy1 = MIN(*sy1, sy);
5326         *sx2 = MAX(*sx2, sx);
5327         *sy2 = MAX(*sy2, sy);
5328       }
5329
5330       num_checked_players++;
5331     }
5332   }
5333 }
5334
5335 static boolean checkIfAllPlayersFitToScreen_RND(void)
5336 {
5337   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5338
5339   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5340
5341   return (sx2 - sx1 < SCR_FIELDX &&
5342           sy2 - sy1 < SCR_FIELDY);
5343 }
5344
5345 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5346 {
5347   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5348
5349   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5350
5351   *sx = (sx1 + sx2) / 2;
5352   *sy = (sy1 + sy2) / 2;
5353 }
5354
5355 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5356                                boolean center_screen, boolean quick_relocation)
5357 {
5358   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5359   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5360   boolean no_delay = (tape.warp_forward);
5361   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5362   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5363   int new_scroll_x, new_scroll_y;
5364
5365   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5366   {
5367     // case 1: quick relocation inside visible screen (without scrolling)
5368
5369     RedrawPlayfield();
5370
5371     return;
5372   }
5373
5374   if (!level.shifted_relocation || center_screen)
5375   {
5376     // relocation _with_ centering of screen
5377
5378     new_scroll_x = SCROLL_POSITION_X(x);
5379     new_scroll_y = SCROLL_POSITION_Y(y);
5380   }
5381   else
5382   {
5383     // relocation _without_ centering of screen
5384
5385     int center_scroll_x = SCROLL_POSITION_X(old_x);
5386     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5387     int offset_x = x + (scroll_x - center_scroll_x);
5388     int offset_y = y + (scroll_y - center_scroll_y);
5389
5390     // for new screen position, apply previous offset to center position
5391     new_scroll_x = SCROLL_POSITION_X(offset_x);
5392     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5393   }
5394
5395   if (quick_relocation)
5396   {
5397     // case 2: quick relocation (redraw without visible scrolling)
5398
5399     scroll_x = new_scroll_x;
5400     scroll_y = new_scroll_y;
5401
5402     RedrawPlayfield();
5403
5404     return;
5405   }
5406
5407   // case 3: visible relocation (with scrolling to new position)
5408
5409   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5410
5411   SetVideoFrameDelay(wait_delay_value);
5412
5413   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5414   {
5415     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5416     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5417
5418     if (dx == 0 && dy == 0)             // no scrolling needed at all
5419       break;
5420
5421     scroll_x -= dx;
5422     scroll_y -= dy;
5423
5424     // set values for horizontal/vertical screen scrolling (half tile size)
5425     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5426     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5427     int pos_x = dx * TILEX / 2;
5428     int pos_y = dy * TILEY / 2;
5429     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5430     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5431
5432     ScrollLevel(dx, dy);
5433     DrawAllPlayers();
5434
5435     // scroll in two steps of half tile size to make things smoother
5436     BlitScreenToBitmapExt_RND(window, fx, fy);
5437
5438     // scroll second step to align at full tile size
5439     BlitScreenToBitmap(window);
5440   }
5441
5442   DrawAllPlayers();
5443   BackToFront();
5444
5445   SetVideoFrameDelay(frame_delay_value_old);
5446 }
5447
5448 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5449 {
5450   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5451   int player_nr = GET_PLAYER_NR(el_player);
5452   struct PlayerInfo *player = &stored_player[player_nr];
5453   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5454   boolean no_delay = (tape.warp_forward);
5455   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5456   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5457   int old_jx = player->jx;
5458   int old_jy = player->jy;
5459   int old_element = Tile[old_jx][old_jy];
5460   int element = Tile[jx][jy];
5461   boolean player_relocated = (old_jx != jx || old_jy != jy);
5462
5463   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5464   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5465   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5466   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5467   int leave_side_horiz = move_dir_horiz;
5468   int leave_side_vert  = move_dir_vert;
5469   int enter_side = enter_side_horiz | enter_side_vert;
5470   int leave_side = leave_side_horiz | leave_side_vert;
5471
5472   if (player->buried)           // do not reanimate dead player
5473     return;
5474
5475   if (!player_relocated)        // no need to relocate the player
5476     return;
5477
5478   if (IS_PLAYER(jx, jy))        // player already placed at new position
5479   {
5480     RemoveField(jx, jy);        // temporarily remove newly placed player
5481     DrawLevelField(jx, jy);
5482   }
5483
5484   if (player->present)
5485   {
5486     while (player->MovPos)
5487     {
5488       ScrollPlayer(player, SCROLL_GO_ON);
5489       ScrollScreen(NULL, SCROLL_GO_ON);
5490
5491       AdvanceFrameAndPlayerCounters(player->index_nr);
5492
5493       DrawPlayer(player);
5494
5495       BackToFront_WithFrameDelay(wait_delay_value);
5496     }
5497
5498     DrawPlayer(player);         // needed here only to cleanup last field
5499     DrawLevelField(player->jx, player->jy);     // remove player graphic
5500
5501     player->is_moving = FALSE;
5502   }
5503
5504   if (IS_CUSTOM_ELEMENT(old_element))
5505     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5506                                CE_LEFT_BY_PLAYER,
5507                                player->index_bit, leave_side);
5508
5509   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5510                                       CE_PLAYER_LEAVES_X,
5511                                       player->index_bit, leave_side);
5512
5513   Tile[jx][jy] = el_player;
5514   InitPlayerField(jx, jy, el_player, TRUE);
5515
5516   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5517      possible that the relocation target field did not contain a player element,
5518      but a walkable element, to which the new player was relocated -- in this
5519      case, restore that (already initialized!) element on the player field */
5520   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5521   {
5522     Tile[jx][jy] = element;     // restore previously existing element
5523   }
5524
5525   // only visually relocate centered player
5526   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5527                      FALSE, level.instant_relocation);
5528
5529   TestIfPlayerTouchesBadThing(jx, jy);
5530   TestIfPlayerTouchesCustomElement(jx, jy);
5531
5532   if (IS_CUSTOM_ELEMENT(element))
5533     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5534                                player->index_bit, enter_side);
5535
5536   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5537                                       player->index_bit, enter_side);
5538
5539   if (player->is_switching)
5540   {
5541     /* ensure that relocation while still switching an element does not cause
5542        a new element to be treated as also switched directly after relocation
5543        (this is important for teleporter switches that teleport the player to
5544        a place where another teleporter switch is in the same direction, which
5545        would then incorrectly be treated as immediately switched before the
5546        direction key that caused the switch was released) */
5547
5548     player->switch_x += jx - old_jx;
5549     player->switch_y += jy - old_jy;
5550   }
5551 }
5552
5553 static void Explode(int ex, int ey, int phase, int mode)
5554 {
5555   int x, y;
5556   int last_phase;
5557   int border_element;
5558
5559   // !!! eliminate this variable !!!
5560   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5561
5562   if (game.explosions_delayed)
5563   {
5564     ExplodeField[ex][ey] = mode;
5565     return;
5566   }
5567
5568   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5569   {
5570     int center_element = Tile[ex][ey];
5571     int artwork_element, explosion_element;     // set these values later
5572
5573     // remove things displayed in background while burning dynamite
5574     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5575       Back[ex][ey] = 0;
5576
5577     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5578     {
5579       // put moving element to center field (and let it explode there)
5580       center_element = MovingOrBlocked2Element(ex, ey);
5581       RemoveMovingField(ex, ey);
5582       Tile[ex][ey] = center_element;
5583     }
5584
5585     // now "center_element" is finally determined -- set related values now
5586     artwork_element = center_element;           // for custom player artwork
5587     explosion_element = center_element;         // for custom player artwork
5588
5589     if (IS_PLAYER(ex, ey))
5590     {
5591       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5592
5593       artwork_element = stored_player[player_nr].artwork_element;
5594
5595       if (level.use_explosion_element[player_nr])
5596       {
5597         explosion_element = level.explosion_element[player_nr];
5598         artwork_element = explosion_element;
5599       }
5600     }
5601
5602     if (mode == EX_TYPE_NORMAL ||
5603         mode == EX_TYPE_CENTER ||
5604         mode == EX_TYPE_CROSS)
5605       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5606
5607     last_phase = element_info[explosion_element].explosion_delay + 1;
5608
5609     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5610     {
5611       int xx = x - ex + 1;
5612       int yy = y - ey + 1;
5613       int element;
5614
5615       if (!IN_LEV_FIELD(x, y) ||
5616           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5617           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5618         continue;
5619
5620       element = Tile[x][y];
5621
5622       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5623       {
5624         element = MovingOrBlocked2Element(x, y);
5625
5626         if (!IS_EXPLOSION_PROOF(element))
5627           RemoveMovingField(x, y);
5628       }
5629
5630       // indestructible elements can only explode in center (but not flames)
5631       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5632                                            mode == EX_TYPE_BORDER)) ||
5633           element == EL_FLAMES)
5634         continue;
5635
5636       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5637          behaviour, for example when touching a yamyam that explodes to rocks
5638          with active deadly shield, a rock is created under the player !!! */
5639       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5640 #if 0
5641       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5642           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5643            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5644 #else
5645       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5646 #endif
5647       {
5648         if (IS_ACTIVE_BOMB(element))
5649         {
5650           // re-activate things under the bomb like gate or penguin
5651           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5652           Back[x][y] = 0;
5653         }
5654
5655         continue;
5656       }
5657
5658       // save walkable background elements while explosion on same tile
5659       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5660           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5661         Back[x][y] = element;
5662
5663       // ignite explodable elements reached by other explosion
5664       if (element == EL_EXPLOSION)
5665         element = Store2[x][y];
5666
5667       if (AmoebaNr[x][y] &&
5668           (element == EL_AMOEBA_FULL ||
5669            element == EL_BD_AMOEBA ||
5670            element == EL_AMOEBA_GROWING))
5671       {
5672         AmoebaCnt[AmoebaNr[x][y]]--;
5673         AmoebaCnt2[AmoebaNr[x][y]]--;
5674       }
5675
5676       RemoveField(x, y);
5677
5678       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5679       {
5680         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5681
5682         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5683
5684         if (PLAYERINFO(ex, ey)->use_murphy)
5685           Store[x][y] = EL_EMPTY;
5686       }
5687
5688       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5689       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5690       else if (ELEM_IS_PLAYER(center_element))
5691         Store[x][y] = EL_EMPTY;
5692       else if (center_element == EL_YAMYAM)
5693         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5694       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5695         Store[x][y] = element_info[center_element].content.e[xx][yy];
5696 #if 1
5697       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5698       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5699       // otherwise) -- FIX THIS !!!
5700       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5701         Store[x][y] = element_info[element].content.e[1][1];
5702 #else
5703       else if (!CAN_EXPLODE(element))
5704         Store[x][y] = element_info[element].content.e[1][1];
5705 #endif
5706       else
5707         Store[x][y] = EL_EMPTY;
5708
5709       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5710           center_element == EL_AMOEBA_TO_DIAMOND)
5711         Store2[x][y] = element;
5712
5713       Tile[x][y] = EL_EXPLOSION;
5714       GfxElement[x][y] = artwork_element;
5715
5716       ExplodePhase[x][y] = 1;
5717       ExplodeDelay[x][y] = last_phase;
5718
5719       Stop[x][y] = TRUE;
5720     }
5721
5722     if (center_element == EL_YAMYAM)
5723       game.yamyam_content_nr =
5724         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5725
5726     return;
5727   }
5728
5729   if (Stop[ex][ey])
5730     return;
5731
5732   x = ex;
5733   y = ey;
5734
5735   if (phase == 1)
5736     GfxFrame[x][y] = 0;         // restart explosion animation
5737
5738   last_phase = ExplodeDelay[x][y];
5739
5740   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5741
5742   // this can happen if the player leaves an explosion just in time
5743   if (GfxElement[x][y] == EL_UNDEFINED)
5744     GfxElement[x][y] = EL_EMPTY;
5745
5746   border_element = Store2[x][y];
5747   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5748     border_element = StorePlayer[x][y];
5749
5750   if (phase == element_info[border_element].ignition_delay ||
5751       phase == last_phase)
5752   {
5753     boolean border_explosion = FALSE;
5754
5755     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5756         !PLAYER_EXPLOSION_PROTECTED(x, y))
5757     {
5758       KillPlayerUnlessExplosionProtected(x, y);
5759       border_explosion = TRUE;
5760     }
5761     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5762     {
5763       Tile[x][y] = Store2[x][y];
5764       Store2[x][y] = 0;
5765       Bang(x, y);
5766       border_explosion = TRUE;
5767     }
5768     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5769     {
5770       AmoebaToDiamond(x, y);
5771       Store2[x][y] = 0;
5772       border_explosion = TRUE;
5773     }
5774
5775     // if an element just explodes due to another explosion (chain-reaction),
5776     // do not immediately end the new explosion when it was the last frame of
5777     // the explosion (as it would be done in the following "if"-statement!)
5778     if (border_explosion && phase == last_phase)
5779       return;
5780   }
5781
5782   if (phase == last_phase)
5783   {
5784     int element;
5785
5786     element = Tile[x][y] = Store[x][y];
5787     Store[x][y] = Store2[x][y] = 0;
5788     GfxElement[x][y] = EL_UNDEFINED;
5789
5790     // player can escape from explosions and might therefore be still alive
5791     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5792         element <= EL_PLAYER_IS_EXPLODING_4)
5793     {
5794       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5795       int explosion_element = EL_PLAYER_1 + player_nr;
5796       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5797       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5798
5799       if (level.use_explosion_element[player_nr])
5800         explosion_element = level.explosion_element[player_nr];
5801
5802       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5803                     element_info[explosion_element].content.e[xx][yy]);
5804     }
5805
5806     // restore probably existing indestructible background element
5807     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5808       element = Tile[x][y] = Back[x][y];
5809     Back[x][y] = 0;
5810
5811     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5812     GfxDir[x][y] = MV_NONE;
5813     ChangeDelay[x][y] = 0;
5814     ChangePage[x][y] = -1;
5815
5816     CustomValue[x][y] = 0;
5817
5818     InitField_WithBug2(x, y, FALSE);
5819
5820     TEST_DrawLevelField(x, y);
5821
5822     TestIfElementTouchesCustomElement(x, y);
5823
5824     if (GFX_CRUMBLED(element))
5825       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5826
5827     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5828       StorePlayer[x][y] = 0;
5829
5830     if (ELEM_IS_PLAYER(element))
5831       RelocatePlayer(x, y, element);
5832   }
5833   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5834   {
5835     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5836     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5837
5838     if (phase == delay)
5839       TEST_DrawLevelFieldCrumbled(x, y);
5840
5841     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5842     {
5843       DrawLevelElement(x, y, Back[x][y]);
5844       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5845     }
5846     else if (IS_WALKABLE_UNDER(Back[x][y]))
5847     {
5848       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5849       DrawLevelElementThruMask(x, y, Back[x][y]);
5850     }
5851     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5852       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5853   }
5854 }
5855
5856 static void DynaExplode(int ex, int ey)
5857 {
5858   int i, j;
5859   int dynabomb_element = Tile[ex][ey];
5860   int dynabomb_size = 1;
5861   boolean dynabomb_xl = FALSE;
5862   struct PlayerInfo *player;
5863   static int xy[4][2] =
5864   {
5865     { 0, -1 },
5866     { -1, 0 },
5867     { +1, 0 },
5868     { 0, +1 }
5869   };
5870
5871   if (IS_ACTIVE_BOMB(dynabomb_element))
5872   {
5873     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5874     dynabomb_size = player->dynabomb_size;
5875     dynabomb_xl = player->dynabomb_xl;
5876     player->dynabombs_left++;
5877   }
5878
5879   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5880
5881   for (i = 0; i < NUM_DIRECTIONS; i++)
5882   {
5883     for (j = 1; j <= dynabomb_size; j++)
5884     {
5885       int x = ex + j * xy[i][0];
5886       int y = ey + j * xy[i][1];
5887       int element;
5888
5889       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
5890         break;
5891
5892       element = Tile[x][y];
5893
5894       // do not restart explosions of fields with active bombs
5895       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5896         continue;
5897
5898       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5899
5900       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5901           !IS_DIGGABLE(element) && !dynabomb_xl)
5902         break;
5903     }
5904   }
5905 }
5906
5907 void Bang(int x, int y)
5908 {
5909   int element = MovingOrBlocked2Element(x, y);
5910   int explosion_type = EX_TYPE_NORMAL;
5911
5912   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5913   {
5914     struct PlayerInfo *player = PLAYERINFO(x, y);
5915
5916     element = Tile[x][y] = player->initial_element;
5917
5918     if (level.use_explosion_element[player->index_nr])
5919     {
5920       int explosion_element = level.explosion_element[player->index_nr];
5921
5922       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5923         explosion_type = EX_TYPE_CROSS;
5924       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5925         explosion_type = EX_TYPE_CENTER;
5926     }
5927   }
5928
5929   switch (element)
5930   {
5931     case EL_BUG:
5932     case EL_SPACESHIP:
5933     case EL_BD_BUTTERFLY:
5934     case EL_BD_FIREFLY:
5935     case EL_YAMYAM:
5936     case EL_DARK_YAMYAM:
5937     case EL_ROBOT:
5938     case EL_PACMAN:
5939     case EL_MOLE:
5940       RaiseScoreElement(element);
5941       break;
5942
5943     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5944     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5945     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5946     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5947     case EL_DYNABOMB_INCREASE_NUMBER:
5948     case EL_DYNABOMB_INCREASE_SIZE:
5949     case EL_DYNABOMB_INCREASE_POWER:
5950       explosion_type = EX_TYPE_DYNA;
5951       break;
5952
5953     case EL_DC_LANDMINE:
5954       explosion_type = EX_TYPE_CENTER;
5955       break;
5956
5957     case EL_PENGUIN:
5958     case EL_LAMP:
5959     case EL_LAMP_ACTIVE:
5960     case EL_AMOEBA_TO_DIAMOND:
5961       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
5962         explosion_type = EX_TYPE_CENTER;
5963       break;
5964
5965     default:
5966       if (element_info[element].explosion_type == EXPLODES_CROSS)
5967         explosion_type = EX_TYPE_CROSS;
5968       else if (element_info[element].explosion_type == EXPLODES_1X1)
5969         explosion_type = EX_TYPE_CENTER;
5970       break;
5971   }
5972
5973   if (explosion_type == EX_TYPE_DYNA)
5974     DynaExplode(x, y);
5975   else
5976     Explode(x, y, EX_PHASE_START, explosion_type);
5977
5978   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5979 }
5980
5981 static void SplashAcid(int x, int y)
5982 {
5983   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5984       (!IN_LEV_FIELD(x - 1, y - 2) ||
5985        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5986     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5987
5988   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5989       (!IN_LEV_FIELD(x + 1, y - 2) ||
5990        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5991     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5992
5993   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5994 }
5995
5996 static void InitBeltMovement(void)
5997 {
5998   static int belt_base_element[4] =
5999   {
6000     EL_CONVEYOR_BELT_1_LEFT,
6001     EL_CONVEYOR_BELT_2_LEFT,
6002     EL_CONVEYOR_BELT_3_LEFT,
6003     EL_CONVEYOR_BELT_4_LEFT
6004   };
6005   static int belt_base_active_element[4] =
6006   {
6007     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6008     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6009     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6010     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6011   };
6012
6013   int x, y, i, j;
6014
6015   // set frame order for belt animation graphic according to belt direction
6016   for (i = 0; i < NUM_BELTS; i++)
6017   {
6018     int belt_nr = i;
6019
6020     for (j = 0; j < NUM_BELT_PARTS; j++)
6021     {
6022       int element = belt_base_active_element[belt_nr] + j;
6023       int graphic_1 = el2img(element);
6024       int graphic_2 = el2panelimg(element);
6025
6026       if (game.belt_dir[i] == MV_LEFT)
6027       {
6028         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6029         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6030       }
6031       else
6032       {
6033         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6034         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6035       }
6036     }
6037   }
6038
6039   SCAN_PLAYFIELD(x, y)
6040   {
6041     int element = Tile[x][y];
6042
6043     for (i = 0; i < NUM_BELTS; i++)
6044     {
6045       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6046       {
6047         int e_belt_nr = getBeltNrFromBeltElement(element);
6048         int belt_nr = i;
6049
6050         if (e_belt_nr == belt_nr)
6051         {
6052           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6053
6054           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6055         }
6056       }
6057     }
6058   }
6059 }
6060
6061 static void ToggleBeltSwitch(int x, int y)
6062 {
6063   static int belt_base_element[4] =
6064   {
6065     EL_CONVEYOR_BELT_1_LEFT,
6066     EL_CONVEYOR_BELT_2_LEFT,
6067     EL_CONVEYOR_BELT_3_LEFT,
6068     EL_CONVEYOR_BELT_4_LEFT
6069   };
6070   static int belt_base_active_element[4] =
6071   {
6072     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6073     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6074     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6075     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6076   };
6077   static int belt_base_switch_element[4] =
6078   {
6079     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6080     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6081     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6082     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6083   };
6084   static int belt_move_dir[4] =
6085   {
6086     MV_LEFT,
6087     MV_NONE,
6088     MV_RIGHT,
6089     MV_NONE,
6090   };
6091
6092   int element = Tile[x][y];
6093   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6094   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6095   int belt_dir = belt_move_dir[belt_dir_nr];
6096   int xx, yy, i;
6097
6098   if (!IS_BELT_SWITCH(element))
6099     return;
6100
6101   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6102   game.belt_dir[belt_nr] = belt_dir;
6103
6104   if (belt_dir_nr == 3)
6105     belt_dir_nr = 1;
6106
6107   // set frame order for belt animation graphic according to belt direction
6108   for (i = 0; i < NUM_BELT_PARTS; i++)
6109   {
6110     int element = belt_base_active_element[belt_nr] + i;
6111     int graphic_1 = el2img(element);
6112     int graphic_2 = el2panelimg(element);
6113
6114     if (belt_dir == MV_LEFT)
6115     {
6116       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6117       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6118     }
6119     else
6120     {
6121       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6122       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6123     }
6124   }
6125
6126   SCAN_PLAYFIELD(xx, yy)
6127   {
6128     int element = Tile[xx][yy];
6129
6130     if (IS_BELT_SWITCH(element))
6131     {
6132       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6133
6134       if (e_belt_nr == belt_nr)
6135       {
6136         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6137         TEST_DrawLevelField(xx, yy);
6138       }
6139     }
6140     else if (IS_BELT(element) && belt_dir != MV_NONE)
6141     {
6142       int e_belt_nr = getBeltNrFromBeltElement(element);
6143
6144       if (e_belt_nr == belt_nr)
6145       {
6146         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6147
6148         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6149         TEST_DrawLevelField(xx, yy);
6150       }
6151     }
6152     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6153     {
6154       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6155
6156       if (e_belt_nr == belt_nr)
6157       {
6158         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6159
6160         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6161         TEST_DrawLevelField(xx, yy);
6162       }
6163     }
6164   }
6165 }
6166
6167 static void ToggleSwitchgateSwitch(int x, int y)
6168 {
6169   int xx, yy;
6170
6171   game.switchgate_pos = !game.switchgate_pos;
6172
6173   SCAN_PLAYFIELD(xx, yy)
6174   {
6175     int element = Tile[xx][yy];
6176
6177     if (element == EL_SWITCHGATE_SWITCH_UP)
6178     {
6179       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6180       TEST_DrawLevelField(xx, yy);
6181     }
6182     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6183     {
6184       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6185       TEST_DrawLevelField(xx, yy);
6186     }
6187     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6188     {
6189       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6190       TEST_DrawLevelField(xx, yy);
6191     }
6192     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6193     {
6194       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6195       TEST_DrawLevelField(xx, yy);
6196     }
6197     else if (element == EL_SWITCHGATE_OPEN ||
6198              element == EL_SWITCHGATE_OPENING)
6199     {
6200       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6201
6202       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6203     }
6204     else if (element == EL_SWITCHGATE_CLOSED ||
6205              element == EL_SWITCHGATE_CLOSING)
6206     {
6207       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6208
6209       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6210     }
6211   }
6212 }
6213
6214 static int getInvisibleActiveFromInvisibleElement(int element)
6215 {
6216   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6217           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6218           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6219           element);
6220 }
6221
6222 static int getInvisibleFromInvisibleActiveElement(int element)
6223 {
6224   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6225           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6226           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6227           element);
6228 }
6229
6230 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6231 {
6232   int x, y;
6233
6234   SCAN_PLAYFIELD(x, y)
6235   {
6236     int element = Tile[x][y];
6237
6238     if (element == EL_LIGHT_SWITCH &&
6239         game.light_time_left > 0)
6240     {
6241       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6242       TEST_DrawLevelField(x, y);
6243     }
6244     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6245              game.light_time_left == 0)
6246     {
6247       Tile[x][y] = EL_LIGHT_SWITCH;
6248       TEST_DrawLevelField(x, y);
6249     }
6250     else if (element == EL_EMC_DRIPPER &&
6251              game.light_time_left > 0)
6252     {
6253       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6254       TEST_DrawLevelField(x, y);
6255     }
6256     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6257              game.light_time_left == 0)
6258     {
6259       Tile[x][y] = EL_EMC_DRIPPER;
6260       TEST_DrawLevelField(x, y);
6261     }
6262     else if (element == EL_INVISIBLE_STEELWALL ||
6263              element == EL_INVISIBLE_WALL ||
6264              element == EL_INVISIBLE_SAND)
6265     {
6266       if (game.light_time_left > 0)
6267         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6268
6269       TEST_DrawLevelField(x, y);
6270
6271       // uncrumble neighbour fields, if needed
6272       if (element == EL_INVISIBLE_SAND)
6273         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6274     }
6275     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6276              element == EL_INVISIBLE_WALL_ACTIVE ||
6277              element == EL_INVISIBLE_SAND_ACTIVE)
6278     {
6279       if (game.light_time_left == 0)
6280         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6281
6282       TEST_DrawLevelField(x, y);
6283
6284       // re-crumble neighbour fields, if needed
6285       if (element == EL_INVISIBLE_SAND)
6286         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6287     }
6288   }
6289 }
6290
6291 static void RedrawAllInvisibleElementsForLenses(void)
6292 {
6293   int x, y;
6294
6295   SCAN_PLAYFIELD(x, y)
6296   {
6297     int element = Tile[x][y];
6298
6299     if (element == EL_EMC_DRIPPER &&
6300         game.lenses_time_left > 0)
6301     {
6302       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6303       TEST_DrawLevelField(x, y);
6304     }
6305     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6306              game.lenses_time_left == 0)
6307     {
6308       Tile[x][y] = EL_EMC_DRIPPER;
6309       TEST_DrawLevelField(x, y);
6310     }
6311     else if (element == EL_INVISIBLE_STEELWALL ||
6312              element == EL_INVISIBLE_WALL ||
6313              element == EL_INVISIBLE_SAND)
6314     {
6315       if (game.lenses_time_left > 0)
6316         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6317
6318       TEST_DrawLevelField(x, y);
6319
6320       // uncrumble neighbour fields, if needed
6321       if (element == EL_INVISIBLE_SAND)
6322         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6323     }
6324     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6325              element == EL_INVISIBLE_WALL_ACTIVE ||
6326              element == EL_INVISIBLE_SAND_ACTIVE)
6327     {
6328       if (game.lenses_time_left == 0)
6329         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6330
6331       TEST_DrawLevelField(x, y);
6332
6333       // re-crumble neighbour fields, if needed
6334       if (element == EL_INVISIBLE_SAND)
6335         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6336     }
6337   }
6338 }
6339
6340 static void RedrawAllInvisibleElementsForMagnifier(void)
6341 {
6342   int x, y;
6343
6344   SCAN_PLAYFIELD(x, y)
6345   {
6346     int element = Tile[x][y];
6347
6348     if (element == EL_EMC_FAKE_GRASS &&
6349         game.magnify_time_left > 0)
6350     {
6351       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6352       TEST_DrawLevelField(x, y);
6353     }
6354     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6355              game.magnify_time_left == 0)
6356     {
6357       Tile[x][y] = EL_EMC_FAKE_GRASS;
6358       TEST_DrawLevelField(x, y);
6359     }
6360     else if (IS_GATE_GRAY(element) &&
6361              game.magnify_time_left > 0)
6362     {
6363       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6364                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6365                     IS_EM_GATE_GRAY(element) ?
6366                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6367                     IS_EMC_GATE_GRAY(element) ?
6368                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6369                     IS_DC_GATE_GRAY(element) ?
6370                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6371                     element);
6372       TEST_DrawLevelField(x, y);
6373     }
6374     else if (IS_GATE_GRAY_ACTIVE(element) &&
6375              game.magnify_time_left == 0)
6376     {
6377       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6378                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6379                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6380                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6381                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6382                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6383                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6384                     EL_DC_GATE_WHITE_GRAY :
6385                     element);
6386       TEST_DrawLevelField(x, y);
6387     }
6388   }
6389 }
6390
6391 static void ToggleLightSwitch(int x, int y)
6392 {
6393   int element = Tile[x][y];
6394
6395   game.light_time_left =
6396     (element == EL_LIGHT_SWITCH ?
6397      level.time_light * FRAMES_PER_SECOND : 0);
6398
6399   RedrawAllLightSwitchesAndInvisibleElements();
6400 }
6401
6402 static void ActivateTimegateSwitch(int x, int y)
6403 {
6404   int xx, yy;
6405
6406   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6407
6408   SCAN_PLAYFIELD(xx, yy)
6409   {
6410     int element = Tile[xx][yy];
6411
6412     if (element == EL_TIMEGATE_CLOSED ||
6413         element == EL_TIMEGATE_CLOSING)
6414     {
6415       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6416       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6417     }
6418
6419     /*
6420     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6421     {
6422       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6423       TEST_DrawLevelField(xx, yy);
6424     }
6425     */
6426
6427   }
6428
6429   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6430                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6431 }
6432
6433 static void Impact(int x, int y)
6434 {
6435   boolean last_line = (y == lev_fieldy - 1);
6436   boolean object_hit = FALSE;
6437   boolean impact = (last_line || object_hit);
6438   int element = Tile[x][y];
6439   int smashed = EL_STEELWALL;
6440
6441   if (!last_line)       // check if element below was hit
6442   {
6443     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6444       return;
6445
6446     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6447                                          MovDir[x][y + 1] != MV_DOWN ||
6448                                          MovPos[x][y + 1] <= TILEY / 2));
6449
6450     // do not smash moving elements that left the smashed field in time
6451     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6452         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6453       object_hit = FALSE;
6454
6455 #if USE_QUICKSAND_IMPACT_BUGFIX
6456     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6457     {
6458       RemoveMovingField(x, y + 1);
6459       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6460       Tile[x][y + 2] = EL_ROCK;
6461       TEST_DrawLevelField(x, y + 2);
6462
6463       object_hit = TRUE;
6464     }
6465
6466     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6467     {
6468       RemoveMovingField(x, y + 1);
6469       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6470       Tile[x][y + 2] = EL_ROCK;
6471       TEST_DrawLevelField(x, y + 2);
6472
6473       object_hit = TRUE;
6474     }
6475 #endif
6476
6477     if (object_hit)
6478       smashed = MovingOrBlocked2Element(x, y + 1);
6479
6480     impact = (last_line || object_hit);
6481   }
6482
6483   if (!last_line && smashed == EL_ACID) // element falls into acid
6484   {
6485     SplashAcid(x, y + 1);
6486     return;
6487   }
6488
6489   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6490   // only reset graphic animation if graphic really changes after impact
6491   if (impact &&
6492       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6493   {
6494     ResetGfxAnimation(x, y);
6495     TEST_DrawLevelField(x, y);
6496   }
6497
6498   if (impact && CAN_EXPLODE_IMPACT(element))
6499   {
6500     Bang(x, y);
6501     return;
6502   }
6503   else if (impact && element == EL_PEARL &&
6504            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6505   {
6506     ResetGfxAnimation(x, y);
6507
6508     Tile[x][y] = EL_PEARL_BREAKING;
6509     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6510     return;
6511   }
6512   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6513   {
6514     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6515
6516     return;
6517   }
6518
6519   if (impact && element == EL_AMOEBA_DROP)
6520   {
6521     if (object_hit && IS_PLAYER(x, y + 1))
6522       KillPlayerUnlessEnemyProtected(x, y + 1);
6523     else if (object_hit && smashed == EL_PENGUIN)
6524       Bang(x, y + 1);
6525     else
6526     {
6527       Tile[x][y] = EL_AMOEBA_GROWING;
6528       Store[x][y] = EL_AMOEBA_WET;
6529
6530       ResetRandomAnimationValue(x, y);
6531     }
6532     return;
6533   }
6534
6535   if (object_hit)               // check which object was hit
6536   {
6537     if ((CAN_PASS_MAGIC_WALL(element) && 
6538          (smashed == EL_MAGIC_WALL ||
6539           smashed == EL_BD_MAGIC_WALL)) ||
6540         (CAN_PASS_DC_MAGIC_WALL(element) &&
6541          smashed == EL_DC_MAGIC_WALL))
6542     {
6543       int xx, yy;
6544       int activated_magic_wall =
6545         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6546          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6547          EL_DC_MAGIC_WALL_ACTIVE);
6548
6549       // activate magic wall / mill
6550       SCAN_PLAYFIELD(xx, yy)
6551       {
6552         if (Tile[xx][yy] == smashed)
6553           Tile[xx][yy] = activated_magic_wall;
6554       }
6555
6556       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6557       game.magic_wall_active = TRUE;
6558
6559       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6560                             SND_MAGIC_WALL_ACTIVATING :
6561                             smashed == EL_BD_MAGIC_WALL ?
6562                             SND_BD_MAGIC_WALL_ACTIVATING :
6563                             SND_DC_MAGIC_WALL_ACTIVATING));
6564     }
6565
6566     if (IS_PLAYER(x, y + 1))
6567     {
6568       if (CAN_SMASH_PLAYER(element))
6569       {
6570         KillPlayerUnlessEnemyProtected(x, y + 1);
6571         return;
6572       }
6573     }
6574     else if (smashed == EL_PENGUIN)
6575     {
6576       if (CAN_SMASH_PLAYER(element))
6577       {
6578         Bang(x, y + 1);
6579         return;
6580       }
6581     }
6582     else if (element == EL_BD_DIAMOND)
6583     {
6584       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6585       {
6586         Bang(x, y + 1);
6587         return;
6588       }
6589     }
6590     else if (((element == EL_SP_INFOTRON ||
6591                element == EL_SP_ZONK) &&
6592               (smashed == EL_SP_SNIKSNAK ||
6593                smashed == EL_SP_ELECTRON ||
6594                smashed == EL_SP_DISK_ORANGE)) ||
6595              (element == EL_SP_INFOTRON &&
6596               smashed == EL_SP_DISK_YELLOW))
6597     {
6598       Bang(x, y + 1);
6599       return;
6600     }
6601     else if (CAN_SMASH_EVERYTHING(element))
6602     {
6603       if (IS_CLASSIC_ENEMY(smashed) ||
6604           CAN_EXPLODE_SMASHED(smashed))
6605       {
6606         Bang(x, y + 1);
6607         return;
6608       }
6609       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6610       {
6611         if (smashed == EL_LAMP ||
6612             smashed == EL_LAMP_ACTIVE)
6613         {
6614           Bang(x, y + 1);
6615           return;
6616         }
6617         else if (smashed == EL_NUT)
6618         {
6619           Tile[x][y + 1] = EL_NUT_BREAKING;
6620           PlayLevelSound(x, y, SND_NUT_BREAKING);
6621           RaiseScoreElement(EL_NUT);
6622           return;
6623         }
6624         else if (smashed == EL_PEARL)
6625         {
6626           ResetGfxAnimation(x, y);
6627
6628           Tile[x][y + 1] = EL_PEARL_BREAKING;
6629           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6630           return;
6631         }
6632         else if (smashed == EL_DIAMOND)
6633         {
6634           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6635           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6636           return;
6637         }
6638         else if (IS_BELT_SWITCH(smashed))
6639         {
6640           ToggleBeltSwitch(x, y + 1);
6641         }
6642         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6643                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6644                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6645                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6646         {
6647           ToggleSwitchgateSwitch(x, y + 1);
6648         }
6649         else if (smashed == EL_LIGHT_SWITCH ||
6650                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6651         {
6652           ToggleLightSwitch(x, y + 1);
6653         }
6654         else
6655         {
6656           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6657
6658           CheckElementChangeBySide(x, y + 1, smashed, element,
6659                                    CE_SWITCHED, CH_SIDE_TOP);
6660           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6661                                             CH_SIDE_TOP);
6662         }
6663       }
6664       else
6665       {
6666         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6667       }
6668     }
6669   }
6670
6671   // play sound of magic wall / mill
6672   if (!last_line &&
6673       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6674        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6675        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6676   {
6677     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6678       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6679     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6680       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6681     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6682       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6683
6684     return;
6685   }
6686
6687   // play sound of object that hits the ground
6688   if (last_line || object_hit)
6689     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6690 }
6691
6692 static void TurnRoundExt(int x, int y)
6693 {
6694   static struct
6695   {
6696     int dx, dy;
6697   } move_xy[] =
6698   {
6699     {  0,  0 },
6700     { -1,  0 },
6701     { +1,  0 },
6702     {  0,  0 },
6703     {  0, -1 },
6704     {  0,  0 }, { 0, 0 }, { 0, 0 },
6705     {  0, +1 }
6706   };
6707   static struct
6708   {
6709     int left, right, back;
6710   } turn[] =
6711   {
6712     { 0,        0,              0        },
6713     { MV_DOWN,  MV_UP,          MV_RIGHT },
6714     { MV_UP,    MV_DOWN,        MV_LEFT  },
6715     { 0,        0,              0        },
6716     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6717     { 0,        0,              0        },
6718     { 0,        0,              0        },
6719     { 0,        0,              0        },
6720     { MV_RIGHT, MV_LEFT,        MV_UP    }
6721   };
6722
6723   int element = Tile[x][y];
6724   int move_pattern = element_info[element].move_pattern;
6725
6726   int old_move_dir = MovDir[x][y];
6727   int left_dir  = turn[old_move_dir].left;
6728   int right_dir = turn[old_move_dir].right;
6729   int back_dir  = turn[old_move_dir].back;
6730
6731   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6732   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6733   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6734   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6735
6736   int left_x  = x + left_dx,  left_y  = y + left_dy;
6737   int right_x = x + right_dx, right_y = y + right_dy;
6738   int move_x  = x + move_dx,  move_y  = y + move_dy;
6739
6740   int xx, yy;
6741
6742   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6743   {
6744     TestIfBadThingTouchesOtherBadThing(x, y);
6745
6746     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6747       MovDir[x][y] = right_dir;
6748     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6749       MovDir[x][y] = left_dir;
6750
6751     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6752       MovDelay[x][y] = 9;
6753     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6754       MovDelay[x][y] = 1;
6755   }
6756   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6757   {
6758     TestIfBadThingTouchesOtherBadThing(x, y);
6759
6760     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6761       MovDir[x][y] = left_dir;
6762     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6763       MovDir[x][y] = right_dir;
6764
6765     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6766       MovDelay[x][y] = 9;
6767     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6768       MovDelay[x][y] = 1;
6769   }
6770   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6771   {
6772     TestIfBadThingTouchesOtherBadThing(x, y);
6773
6774     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6775       MovDir[x][y] = left_dir;
6776     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6777       MovDir[x][y] = right_dir;
6778
6779     if (MovDir[x][y] != old_move_dir)
6780       MovDelay[x][y] = 9;
6781   }
6782   else if (element == EL_YAMYAM)
6783   {
6784     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6785     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6786
6787     if (can_turn_left && can_turn_right)
6788       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6789     else if (can_turn_left)
6790       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6791     else if (can_turn_right)
6792       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6793     else
6794       MovDir[x][y] = back_dir;
6795
6796     MovDelay[x][y] = 16 + 16 * RND(3);
6797   }
6798   else if (element == EL_DARK_YAMYAM)
6799   {
6800     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6801                                                          left_x, left_y);
6802     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6803                                                          right_x, right_y);
6804
6805     if (can_turn_left && can_turn_right)
6806       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6807     else if (can_turn_left)
6808       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6809     else if (can_turn_right)
6810       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6811     else
6812       MovDir[x][y] = back_dir;
6813
6814     MovDelay[x][y] = 16 + 16 * RND(3);
6815   }
6816   else if (element == EL_PACMAN)
6817   {
6818     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6819     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6820
6821     if (can_turn_left && can_turn_right)
6822       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6823     else if (can_turn_left)
6824       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6825     else if (can_turn_right)
6826       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6827     else
6828       MovDir[x][y] = back_dir;
6829
6830     MovDelay[x][y] = 6 + RND(40);
6831   }
6832   else if (element == EL_PIG)
6833   {
6834     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6835     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6836     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6837     boolean should_turn_left, should_turn_right, should_move_on;
6838     int rnd_value = 24;
6839     int rnd = RND(rnd_value);
6840
6841     should_turn_left = (can_turn_left &&
6842                         (!can_move_on ||
6843                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6844                                                    y + back_dy + left_dy)));
6845     should_turn_right = (can_turn_right &&
6846                          (!can_move_on ||
6847                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6848                                                     y + back_dy + right_dy)));
6849     should_move_on = (can_move_on &&
6850                       (!can_turn_left ||
6851                        !can_turn_right ||
6852                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6853                                                  y + move_dy + left_dy) ||
6854                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6855                                                  y + move_dy + right_dy)));
6856
6857     if (should_turn_left || should_turn_right || should_move_on)
6858     {
6859       if (should_turn_left && should_turn_right && should_move_on)
6860         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6861                         rnd < 2 * rnd_value / 3 ? right_dir :
6862                         old_move_dir);
6863       else if (should_turn_left && should_turn_right)
6864         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6865       else if (should_turn_left && should_move_on)
6866         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6867       else if (should_turn_right && should_move_on)
6868         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6869       else if (should_turn_left)
6870         MovDir[x][y] = left_dir;
6871       else if (should_turn_right)
6872         MovDir[x][y] = right_dir;
6873       else if (should_move_on)
6874         MovDir[x][y] = old_move_dir;
6875     }
6876     else if (can_move_on && rnd > rnd_value / 8)
6877       MovDir[x][y] = old_move_dir;
6878     else if (can_turn_left && can_turn_right)
6879       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6880     else if (can_turn_left && rnd > rnd_value / 8)
6881       MovDir[x][y] = left_dir;
6882     else if (can_turn_right && rnd > rnd_value/8)
6883       MovDir[x][y] = right_dir;
6884     else
6885       MovDir[x][y] = back_dir;
6886
6887     xx = x + move_xy[MovDir[x][y]].dx;
6888     yy = y + move_xy[MovDir[x][y]].dy;
6889
6890     if (!IN_LEV_FIELD(xx, yy) ||
6891         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
6892       MovDir[x][y] = old_move_dir;
6893
6894     MovDelay[x][y] = 0;
6895   }
6896   else if (element == EL_DRAGON)
6897   {
6898     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6899     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6900     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6901     int rnd_value = 24;
6902     int rnd = RND(rnd_value);
6903
6904     if (can_move_on && rnd > rnd_value / 8)
6905       MovDir[x][y] = old_move_dir;
6906     else if (can_turn_left && can_turn_right)
6907       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6908     else if (can_turn_left && rnd > rnd_value / 8)
6909       MovDir[x][y] = left_dir;
6910     else if (can_turn_right && rnd > rnd_value / 8)
6911       MovDir[x][y] = right_dir;
6912     else
6913       MovDir[x][y] = back_dir;
6914
6915     xx = x + move_xy[MovDir[x][y]].dx;
6916     yy = y + move_xy[MovDir[x][y]].dy;
6917
6918     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6919       MovDir[x][y] = old_move_dir;
6920
6921     MovDelay[x][y] = 0;
6922   }
6923   else if (element == EL_MOLE)
6924   {
6925     boolean can_move_on =
6926       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6927                             IS_AMOEBOID(Tile[move_x][move_y]) ||
6928                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
6929     if (!can_move_on)
6930     {
6931       boolean can_turn_left =
6932         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6933                               IS_AMOEBOID(Tile[left_x][left_y])));
6934
6935       boolean can_turn_right =
6936         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6937                               IS_AMOEBOID(Tile[right_x][right_y])));
6938
6939       if (can_turn_left && can_turn_right)
6940         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6941       else if (can_turn_left)
6942         MovDir[x][y] = left_dir;
6943       else
6944         MovDir[x][y] = right_dir;
6945     }
6946
6947     if (MovDir[x][y] != old_move_dir)
6948       MovDelay[x][y] = 9;
6949   }
6950   else if (element == EL_BALLOON)
6951   {
6952     MovDir[x][y] = game.wind_direction;
6953     MovDelay[x][y] = 0;
6954   }
6955   else if (element == EL_SPRING)
6956   {
6957     if (MovDir[x][y] & MV_HORIZONTAL)
6958     {
6959       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6960           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6961       {
6962         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6963         ResetGfxAnimation(move_x, move_y);
6964         TEST_DrawLevelField(move_x, move_y);
6965
6966         MovDir[x][y] = back_dir;
6967       }
6968       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6969                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6970         MovDir[x][y] = MV_NONE;
6971     }
6972
6973     MovDelay[x][y] = 0;
6974   }
6975   else if (element == EL_ROBOT ||
6976            element == EL_SATELLITE ||
6977            element == EL_PENGUIN ||
6978            element == EL_EMC_ANDROID)
6979   {
6980     int attr_x = -1, attr_y = -1;
6981
6982     if (game.all_players_gone)
6983     {
6984       attr_x = game.exit_x;
6985       attr_y = game.exit_y;
6986     }
6987     else
6988     {
6989       int i;
6990
6991       for (i = 0; i < MAX_PLAYERS; i++)
6992       {
6993         struct PlayerInfo *player = &stored_player[i];
6994         int jx = player->jx, jy = player->jy;
6995
6996         if (!player->active)
6997           continue;
6998
6999         if (attr_x == -1 ||
7000             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7001         {
7002           attr_x = jx;
7003           attr_y = jy;
7004         }
7005       }
7006     }
7007
7008     if (element == EL_ROBOT &&
7009         game.robot_wheel_x >= 0 &&
7010         game.robot_wheel_y >= 0 &&
7011         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7012          game.engine_version < VERSION_IDENT(3,1,0,0)))
7013     {
7014       attr_x = game.robot_wheel_x;
7015       attr_y = game.robot_wheel_y;
7016     }
7017
7018     if (element == EL_PENGUIN)
7019     {
7020       int i;
7021       static int xy[4][2] =
7022       {
7023         { 0, -1 },
7024         { -1, 0 },
7025         { +1, 0 },
7026         { 0, +1 }
7027       };
7028
7029       for (i = 0; i < NUM_DIRECTIONS; i++)
7030       {
7031         int ex = x + xy[i][0];
7032         int ey = y + xy[i][1];
7033
7034         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7035                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7036                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7037                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7038         {
7039           attr_x = ex;
7040           attr_y = ey;
7041           break;
7042         }
7043       }
7044     }
7045
7046     MovDir[x][y] = MV_NONE;
7047     if (attr_x < x)
7048       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7049     else if (attr_x > x)
7050       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7051     if (attr_y < y)
7052       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7053     else if (attr_y > y)
7054       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7055
7056     if (element == EL_ROBOT)
7057     {
7058       int newx, newy;
7059
7060       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7061         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7062       Moving2Blocked(x, y, &newx, &newy);
7063
7064       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7065         MovDelay[x][y] = 8 + 8 * !RND(3);
7066       else
7067         MovDelay[x][y] = 16;
7068     }
7069     else if (element == EL_PENGUIN)
7070     {
7071       int newx, newy;
7072
7073       MovDelay[x][y] = 1;
7074
7075       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7076       {
7077         boolean first_horiz = RND(2);
7078         int new_move_dir = MovDir[x][y];
7079
7080         MovDir[x][y] =
7081           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7082         Moving2Blocked(x, y, &newx, &newy);
7083
7084         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7085           return;
7086
7087         MovDir[x][y] =
7088           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7089         Moving2Blocked(x, y, &newx, &newy);
7090
7091         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7092           return;
7093
7094         MovDir[x][y] = old_move_dir;
7095         return;
7096       }
7097     }
7098     else if (element == EL_SATELLITE)
7099     {
7100       int newx, newy;
7101
7102       MovDelay[x][y] = 1;
7103
7104       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7105       {
7106         boolean first_horiz = RND(2);
7107         int new_move_dir = MovDir[x][y];
7108
7109         MovDir[x][y] =
7110           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7111         Moving2Blocked(x, y, &newx, &newy);
7112
7113         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7114           return;
7115
7116         MovDir[x][y] =
7117           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7118         Moving2Blocked(x, y, &newx, &newy);
7119
7120         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7121           return;
7122
7123         MovDir[x][y] = old_move_dir;
7124         return;
7125       }
7126     }
7127     else if (element == EL_EMC_ANDROID)
7128     {
7129       static int check_pos[16] =
7130       {
7131         -1,             //  0 => (invalid)
7132         7,              //  1 => MV_LEFT
7133         3,              //  2 => MV_RIGHT
7134         -1,             //  3 => (invalid)
7135         1,              //  4 =>            MV_UP
7136         0,              //  5 => MV_LEFT  | MV_UP
7137         2,              //  6 => MV_RIGHT | MV_UP
7138         -1,             //  7 => (invalid)
7139         5,              //  8 =>            MV_DOWN
7140         6,              //  9 => MV_LEFT  | MV_DOWN
7141         4,              // 10 => MV_RIGHT | MV_DOWN
7142         -1,             // 11 => (invalid)
7143         -1,             // 12 => (invalid)
7144         -1,             // 13 => (invalid)
7145         -1,             // 14 => (invalid)
7146         -1,             // 15 => (invalid)
7147       };
7148       static struct
7149       {
7150         int dx, dy;
7151         int dir;
7152       } check_xy[8] =
7153       {
7154         { -1, -1,       MV_LEFT  | MV_UP   },
7155         {  0, -1,                  MV_UP   },
7156         { +1, -1,       MV_RIGHT | MV_UP   },
7157         { +1,  0,       MV_RIGHT           },
7158         { +1, +1,       MV_RIGHT | MV_DOWN },
7159         {  0, +1,                  MV_DOWN },
7160         { -1, +1,       MV_LEFT  | MV_DOWN },
7161         { -1,  0,       MV_LEFT            },
7162       };
7163       int start_pos, check_order;
7164       boolean can_clone = FALSE;
7165       int i;
7166
7167       // check if there is any free field around current position
7168       for (i = 0; i < 8; i++)
7169       {
7170         int newx = x + check_xy[i].dx;
7171         int newy = y + check_xy[i].dy;
7172
7173         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7174         {
7175           can_clone = TRUE;
7176
7177           break;
7178         }
7179       }
7180
7181       if (can_clone)            // randomly find an element to clone
7182       {
7183         can_clone = FALSE;
7184
7185         start_pos = check_pos[RND(8)];
7186         check_order = (RND(2) ? -1 : +1);
7187
7188         for (i = 0; i < 8; i++)
7189         {
7190           int pos_raw = start_pos + i * check_order;
7191           int pos = (pos_raw + 8) % 8;
7192           int newx = x + check_xy[pos].dx;
7193           int newy = y + check_xy[pos].dy;
7194
7195           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7196           {
7197             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7198             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7199
7200             Store[x][y] = Tile[newx][newy];
7201
7202             can_clone = TRUE;
7203
7204             break;
7205           }
7206         }
7207       }
7208
7209       if (can_clone)            // randomly find a direction to move
7210       {
7211         can_clone = FALSE;
7212
7213         start_pos = check_pos[RND(8)];
7214         check_order = (RND(2) ? -1 : +1);
7215
7216         for (i = 0; i < 8; i++)
7217         {
7218           int pos_raw = start_pos + i * check_order;
7219           int pos = (pos_raw + 8) % 8;
7220           int newx = x + check_xy[pos].dx;
7221           int newy = y + check_xy[pos].dy;
7222           int new_move_dir = check_xy[pos].dir;
7223
7224           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7225           {
7226             MovDir[x][y] = new_move_dir;
7227             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7228
7229             can_clone = TRUE;
7230
7231             break;
7232           }
7233         }
7234       }
7235
7236       if (can_clone)            // cloning and moving successful
7237         return;
7238
7239       // cannot clone -- try to move towards player
7240
7241       start_pos = check_pos[MovDir[x][y] & 0x0f];
7242       check_order = (RND(2) ? -1 : +1);
7243
7244       for (i = 0; i < 3; i++)
7245       {
7246         // first check start_pos, then previous/next or (next/previous) pos
7247         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7248         int pos = (pos_raw + 8) % 8;
7249         int newx = x + check_xy[pos].dx;
7250         int newy = y + check_xy[pos].dy;
7251         int new_move_dir = check_xy[pos].dir;
7252
7253         if (IS_PLAYER(newx, newy))
7254           break;
7255
7256         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7257         {
7258           MovDir[x][y] = new_move_dir;
7259           MovDelay[x][y] = level.android_move_time * 8 + 1;
7260
7261           break;
7262         }
7263       }
7264     }
7265   }
7266   else if (move_pattern == MV_TURNING_LEFT ||
7267            move_pattern == MV_TURNING_RIGHT ||
7268            move_pattern == MV_TURNING_LEFT_RIGHT ||
7269            move_pattern == MV_TURNING_RIGHT_LEFT ||
7270            move_pattern == MV_TURNING_RANDOM ||
7271            move_pattern == MV_ALL_DIRECTIONS)
7272   {
7273     boolean can_turn_left =
7274       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7275     boolean can_turn_right =
7276       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7277
7278     if (element_info[element].move_stepsize == 0)       // "not moving"
7279       return;
7280
7281     if (move_pattern == MV_TURNING_LEFT)
7282       MovDir[x][y] = left_dir;
7283     else if (move_pattern == MV_TURNING_RIGHT)
7284       MovDir[x][y] = right_dir;
7285     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7286       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7287     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7288       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7289     else if (move_pattern == MV_TURNING_RANDOM)
7290       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7291                       can_turn_right && !can_turn_left ? right_dir :
7292                       RND(2) ? left_dir : right_dir);
7293     else if (can_turn_left && can_turn_right)
7294       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7295     else if (can_turn_left)
7296       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7297     else if (can_turn_right)
7298       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7299     else
7300       MovDir[x][y] = back_dir;
7301
7302     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7303   }
7304   else if (move_pattern == MV_HORIZONTAL ||
7305            move_pattern == MV_VERTICAL)
7306   {
7307     if (move_pattern & old_move_dir)
7308       MovDir[x][y] = back_dir;
7309     else if (move_pattern == MV_HORIZONTAL)
7310       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7311     else if (move_pattern == MV_VERTICAL)
7312       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7313
7314     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7315   }
7316   else if (move_pattern & MV_ANY_DIRECTION)
7317   {
7318     MovDir[x][y] = move_pattern;
7319     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7320   }
7321   else if (move_pattern & MV_WIND_DIRECTION)
7322   {
7323     MovDir[x][y] = game.wind_direction;
7324     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7325   }
7326   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7327   {
7328     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7329       MovDir[x][y] = left_dir;
7330     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7331       MovDir[x][y] = right_dir;
7332
7333     if (MovDir[x][y] != old_move_dir)
7334       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7335   }
7336   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7337   {
7338     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7339       MovDir[x][y] = right_dir;
7340     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7341       MovDir[x][y] = left_dir;
7342
7343     if (MovDir[x][y] != old_move_dir)
7344       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7345   }
7346   else if (move_pattern == MV_TOWARDS_PLAYER ||
7347            move_pattern == MV_AWAY_FROM_PLAYER)
7348   {
7349     int attr_x = -1, attr_y = -1;
7350     int newx, newy;
7351     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7352
7353     if (game.all_players_gone)
7354     {
7355       attr_x = game.exit_x;
7356       attr_y = game.exit_y;
7357     }
7358     else
7359     {
7360       int i;
7361
7362       for (i = 0; i < MAX_PLAYERS; i++)
7363       {
7364         struct PlayerInfo *player = &stored_player[i];
7365         int jx = player->jx, jy = player->jy;
7366
7367         if (!player->active)
7368           continue;
7369
7370         if (attr_x == -1 ||
7371             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7372         {
7373           attr_x = jx;
7374           attr_y = jy;
7375         }
7376       }
7377     }
7378
7379     MovDir[x][y] = MV_NONE;
7380     if (attr_x < x)
7381       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7382     else if (attr_x > x)
7383       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7384     if (attr_y < y)
7385       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7386     else if (attr_y > y)
7387       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7388
7389     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7390
7391     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7392     {
7393       boolean first_horiz = RND(2);
7394       int new_move_dir = MovDir[x][y];
7395
7396       if (element_info[element].move_stepsize == 0)     // "not moving"
7397       {
7398         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7399         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7400
7401         return;
7402       }
7403
7404       MovDir[x][y] =
7405         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7406       Moving2Blocked(x, y, &newx, &newy);
7407
7408       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7409         return;
7410
7411       MovDir[x][y] =
7412         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7413       Moving2Blocked(x, y, &newx, &newy);
7414
7415       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7416         return;
7417
7418       MovDir[x][y] = old_move_dir;
7419     }
7420   }
7421   else if (move_pattern == MV_WHEN_PUSHED ||
7422            move_pattern == MV_WHEN_DROPPED)
7423   {
7424     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7425       MovDir[x][y] = MV_NONE;
7426
7427     MovDelay[x][y] = 0;
7428   }
7429   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7430   {
7431     static int test_xy[7][2] =
7432     {
7433       { 0, -1 },
7434       { -1, 0 },
7435       { +1, 0 },
7436       { 0, +1 },
7437       { 0, -1 },
7438       { -1, 0 },
7439       { +1, 0 },
7440     };
7441     static int test_dir[7] =
7442     {
7443       MV_UP,
7444       MV_LEFT,
7445       MV_RIGHT,
7446       MV_DOWN,
7447       MV_UP,
7448       MV_LEFT,
7449       MV_RIGHT,
7450     };
7451     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7452     int move_preference = -1000000;     // start with very low preference
7453     int new_move_dir = MV_NONE;
7454     int start_test = RND(4);
7455     int i;
7456
7457     for (i = 0; i < NUM_DIRECTIONS; i++)
7458     {
7459       int move_dir = test_dir[start_test + i];
7460       int move_dir_preference;
7461
7462       xx = x + test_xy[start_test + i][0];
7463       yy = y + test_xy[start_test + i][1];
7464
7465       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7466           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7467       {
7468         new_move_dir = move_dir;
7469
7470         break;
7471       }
7472
7473       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7474         continue;
7475
7476       move_dir_preference = -1 * RunnerVisit[xx][yy];
7477       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7478         move_dir_preference = PlayerVisit[xx][yy];
7479
7480       if (move_dir_preference > move_preference)
7481       {
7482         // prefer field that has not been visited for the longest time
7483         move_preference = move_dir_preference;
7484         new_move_dir = move_dir;
7485       }
7486       else if (move_dir_preference == move_preference &&
7487                move_dir == old_move_dir)
7488       {
7489         // prefer last direction when all directions are preferred equally
7490         move_preference = move_dir_preference;
7491         new_move_dir = move_dir;
7492       }
7493     }
7494
7495     MovDir[x][y] = new_move_dir;
7496     if (old_move_dir != new_move_dir)
7497       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7498   }
7499 }
7500
7501 static void TurnRound(int x, int y)
7502 {
7503   int direction = MovDir[x][y];
7504
7505   TurnRoundExt(x, y);
7506
7507   GfxDir[x][y] = MovDir[x][y];
7508
7509   if (direction != MovDir[x][y])
7510     GfxFrame[x][y] = 0;
7511
7512   if (MovDelay[x][y])
7513     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7514
7515   ResetGfxFrame(x, y);
7516 }
7517
7518 static boolean JustBeingPushed(int x, int y)
7519 {
7520   int i;
7521
7522   for (i = 0; i < MAX_PLAYERS; i++)
7523   {
7524     struct PlayerInfo *player = &stored_player[i];
7525
7526     if (player->active && player->is_pushing && player->MovPos)
7527     {
7528       int next_jx = player->jx + (player->jx - player->last_jx);
7529       int next_jy = player->jy + (player->jy - player->last_jy);
7530
7531       if (x == next_jx && y == next_jy)
7532         return TRUE;
7533     }
7534   }
7535
7536   return FALSE;
7537 }
7538
7539 static void StartMoving(int x, int y)
7540 {
7541   boolean started_moving = FALSE;       // some elements can fall _and_ move
7542   int element = Tile[x][y];
7543
7544   if (Stop[x][y])
7545     return;
7546
7547   if (MovDelay[x][y] == 0)
7548     GfxAction[x][y] = ACTION_DEFAULT;
7549
7550   if (CAN_FALL(element) && y < lev_fieldy - 1)
7551   {
7552     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7553         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7554       if (JustBeingPushed(x, y))
7555         return;
7556
7557     if (element == EL_QUICKSAND_FULL)
7558     {
7559       if (IS_FREE(x, y + 1))
7560       {
7561         InitMovingField(x, y, MV_DOWN);
7562         started_moving = TRUE;
7563
7564         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7565 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7566         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7567           Store[x][y] = EL_ROCK;
7568 #else
7569         Store[x][y] = EL_ROCK;
7570 #endif
7571
7572         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7573       }
7574       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7575       {
7576         if (!MovDelay[x][y])
7577         {
7578           MovDelay[x][y] = TILEY + 1;
7579
7580           ResetGfxAnimation(x, y);
7581           ResetGfxAnimation(x, y + 1);
7582         }
7583
7584         if (MovDelay[x][y])
7585         {
7586           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7587           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7588
7589           MovDelay[x][y]--;
7590           if (MovDelay[x][y])
7591             return;
7592         }
7593
7594         Tile[x][y] = EL_QUICKSAND_EMPTY;
7595         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7596         Store[x][y + 1] = Store[x][y];
7597         Store[x][y] = 0;
7598
7599         PlayLevelSoundAction(x, y, ACTION_FILLING);
7600       }
7601       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7602       {
7603         if (!MovDelay[x][y])
7604         {
7605           MovDelay[x][y] = TILEY + 1;
7606
7607           ResetGfxAnimation(x, y);
7608           ResetGfxAnimation(x, y + 1);
7609         }
7610
7611         if (MovDelay[x][y])
7612         {
7613           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7614           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7615
7616           MovDelay[x][y]--;
7617           if (MovDelay[x][y])
7618             return;
7619         }
7620
7621         Tile[x][y] = EL_QUICKSAND_EMPTY;
7622         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7623         Store[x][y + 1] = Store[x][y];
7624         Store[x][y] = 0;
7625
7626         PlayLevelSoundAction(x, y, ACTION_FILLING);
7627       }
7628     }
7629     else if (element == EL_QUICKSAND_FAST_FULL)
7630     {
7631       if (IS_FREE(x, y + 1))
7632       {
7633         InitMovingField(x, y, MV_DOWN);
7634         started_moving = TRUE;
7635
7636         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7637 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7638         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7639           Store[x][y] = EL_ROCK;
7640 #else
7641         Store[x][y] = EL_ROCK;
7642 #endif
7643
7644         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
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_FAST_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_FAST_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       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7674       {
7675         if (!MovDelay[x][y])
7676         {
7677           MovDelay[x][y] = TILEY + 1;
7678
7679           ResetGfxAnimation(x, y);
7680           ResetGfxAnimation(x, y + 1);
7681         }
7682
7683         if (MovDelay[x][y])
7684         {
7685           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7686           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7687
7688           MovDelay[x][y]--;
7689           if (MovDelay[x][y])
7690             return;
7691         }
7692
7693         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7694         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7695         Store[x][y + 1] = Store[x][y];
7696         Store[x][y] = 0;
7697
7698         PlayLevelSoundAction(x, y, ACTION_FILLING);
7699       }
7700     }
7701     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7702              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7703     {
7704       InitMovingField(x, y, MV_DOWN);
7705       started_moving = TRUE;
7706
7707       Tile[x][y] = EL_QUICKSAND_FILLING;
7708       Store[x][y] = element;
7709
7710       PlayLevelSoundAction(x, y, ACTION_FILLING);
7711     }
7712     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7713              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7714     {
7715       InitMovingField(x, y, MV_DOWN);
7716       started_moving = TRUE;
7717
7718       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7719       Store[x][y] = element;
7720
7721       PlayLevelSoundAction(x, y, ACTION_FILLING);
7722     }
7723     else if (element == EL_MAGIC_WALL_FULL)
7724     {
7725       if (IS_FREE(x, y + 1))
7726       {
7727         InitMovingField(x, y, MV_DOWN);
7728         started_moving = TRUE;
7729
7730         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7731         Store[x][y] = EL_CHANGED(Store[x][y]);
7732       }
7733       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7734       {
7735         if (!MovDelay[x][y])
7736           MovDelay[x][y] = TILEY / 4 + 1;
7737
7738         if (MovDelay[x][y])
7739         {
7740           MovDelay[x][y]--;
7741           if (MovDelay[x][y])
7742             return;
7743         }
7744
7745         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7746         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7747         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7748         Store[x][y] = 0;
7749       }
7750     }
7751     else if (element == EL_BD_MAGIC_WALL_FULL)
7752     {
7753       if (IS_FREE(x, y + 1))
7754       {
7755         InitMovingField(x, y, MV_DOWN);
7756         started_moving = TRUE;
7757
7758         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7759         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7760       }
7761       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7762       {
7763         if (!MovDelay[x][y])
7764           MovDelay[x][y] = TILEY / 4 + 1;
7765
7766         if (MovDelay[x][y])
7767         {
7768           MovDelay[x][y]--;
7769           if (MovDelay[x][y])
7770             return;
7771         }
7772
7773         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7774         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7775         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7776         Store[x][y] = 0;
7777       }
7778     }
7779     else if (element == EL_DC_MAGIC_WALL_FULL)
7780     {
7781       if (IS_FREE(x, y + 1))
7782       {
7783         InitMovingField(x, y, MV_DOWN);
7784         started_moving = TRUE;
7785
7786         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7787         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7788       }
7789       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7790       {
7791         if (!MovDelay[x][y])
7792           MovDelay[x][y] = TILEY / 4 + 1;
7793
7794         if (MovDelay[x][y])
7795         {
7796           MovDelay[x][y]--;
7797           if (MovDelay[x][y])
7798             return;
7799         }
7800
7801         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7802         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7803         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7804         Store[x][y] = 0;
7805       }
7806     }
7807     else if ((CAN_PASS_MAGIC_WALL(element) &&
7808               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7809                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7810              (CAN_PASS_DC_MAGIC_WALL(element) &&
7811               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7812
7813     {
7814       InitMovingField(x, y, MV_DOWN);
7815       started_moving = TRUE;
7816
7817       Tile[x][y] =
7818         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7819          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7820          EL_DC_MAGIC_WALL_FILLING);
7821       Store[x][y] = element;
7822     }
7823     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
7824     {
7825       SplashAcid(x, y + 1);
7826
7827       InitMovingField(x, y, MV_DOWN);
7828       started_moving = TRUE;
7829
7830       Store[x][y] = EL_ACID;
7831     }
7832     else if (
7833              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7834               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7835              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7836               CAN_FALL(element) && WasJustFalling[x][y] &&
7837               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7838
7839              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7840               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7841               (Tile[x][y + 1] == EL_BLOCKED)))
7842     {
7843       /* this is needed for a special case not covered by calling "Impact()"
7844          from "ContinueMoving()": if an element moves to a tile directly below
7845          another element which was just falling on that tile (which was empty
7846          in the previous frame), the falling element above would just stop
7847          instead of smashing the element below (in previous version, the above
7848          element was just checked for "moving" instead of "falling", resulting
7849          in incorrect smashes caused by horizontal movement of the above
7850          element; also, the case of the player being the element to smash was
7851          simply not covered here... :-/ ) */
7852
7853       CheckCollision[x][y] = 0;
7854       CheckImpact[x][y] = 0;
7855
7856       Impact(x, y);
7857     }
7858     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7859     {
7860       if (MovDir[x][y] == MV_NONE)
7861       {
7862         InitMovingField(x, y, MV_DOWN);
7863         started_moving = TRUE;
7864       }
7865     }
7866     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
7867     {
7868       if (WasJustFalling[x][y]) // prevent animation from being restarted
7869         MovDir[x][y] = MV_DOWN;
7870
7871       InitMovingField(x, y, MV_DOWN);
7872       started_moving = TRUE;
7873     }
7874     else if (element == EL_AMOEBA_DROP)
7875     {
7876       Tile[x][y] = EL_AMOEBA_GROWING;
7877       Store[x][y] = EL_AMOEBA_WET;
7878     }
7879     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7880               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
7881              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7882              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7883     {
7884       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7885                                 (IS_FREE(x - 1, y + 1) ||
7886                                  Tile[x - 1][y + 1] == EL_ACID));
7887       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7888                                 (IS_FREE(x + 1, y + 1) ||
7889                                  Tile[x + 1][y + 1] == EL_ACID));
7890       boolean can_fall_any  = (can_fall_left || can_fall_right);
7891       boolean can_fall_both = (can_fall_left && can_fall_right);
7892       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
7893
7894       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7895       {
7896         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7897           can_fall_right = FALSE;
7898         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7899           can_fall_left = FALSE;
7900         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7901           can_fall_right = FALSE;
7902         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7903           can_fall_left = FALSE;
7904
7905         can_fall_any  = (can_fall_left || can_fall_right);
7906         can_fall_both = FALSE;
7907       }
7908
7909       if (can_fall_both)
7910       {
7911         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7912           can_fall_right = FALSE;       // slip down on left side
7913         else
7914           can_fall_left = !(can_fall_right = RND(2));
7915
7916         can_fall_both = FALSE;
7917       }
7918
7919       if (can_fall_any)
7920       {
7921         // if not determined otherwise, prefer left side for slipping down
7922         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7923         started_moving = TRUE;
7924       }
7925     }
7926     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
7927     {
7928       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7929       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7930       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
7931       int belt_dir = game.belt_dir[belt_nr];
7932
7933       if ((belt_dir == MV_LEFT  && left_is_free) ||
7934           (belt_dir == MV_RIGHT && right_is_free))
7935       {
7936         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7937
7938         InitMovingField(x, y, belt_dir);
7939         started_moving = TRUE;
7940
7941         Pushed[x][y] = TRUE;
7942         Pushed[nextx][y] = TRUE;
7943
7944         GfxAction[x][y] = ACTION_DEFAULT;
7945       }
7946       else
7947       {
7948         MovDir[x][y] = 0;       // if element was moving, stop it
7949       }
7950     }
7951   }
7952
7953   // not "else if" because of elements that can fall and move (EL_SPRING)
7954   if (CAN_MOVE(element) && !started_moving)
7955   {
7956     int move_pattern = element_info[element].move_pattern;
7957     int newx, newy;
7958
7959     Moving2Blocked(x, y, &newx, &newy);
7960
7961     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7962       return;
7963
7964     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7965         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7966     {
7967       WasJustMoving[x][y] = 0;
7968       CheckCollision[x][y] = 0;
7969
7970       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7971
7972       if (Tile[x][y] != element)        // element has changed
7973         return;
7974     }
7975
7976     if (!MovDelay[x][y])        // start new movement phase
7977     {
7978       // all objects that can change their move direction after each step
7979       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
7980
7981       if (element != EL_YAMYAM &&
7982           element != EL_DARK_YAMYAM &&
7983           element != EL_PACMAN &&
7984           !(move_pattern & MV_ANY_DIRECTION) &&
7985           move_pattern != MV_TURNING_LEFT &&
7986           move_pattern != MV_TURNING_RIGHT &&
7987           move_pattern != MV_TURNING_LEFT_RIGHT &&
7988           move_pattern != MV_TURNING_RIGHT_LEFT &&
7989           move_pattern != MV_TURNING_RANDOM)
7990       {
7991         TurnRound(x, y);
7992
7993         if (MovDelay[x][y] && (element == EL_BUG ||
7994                                element == EL_SPACESHIP ||
7995                                element == EL_SP_SNIKSNAK ||
7996                                element == EL_SP_ELECTRON ||
7997                                element == EL_MOLE))
7998           TEST_DrawLevelField(x, y);
7999       }
8000     }
8001
8002     if (MovDelay[x][y])         // wait some time before next movement
8003     {
8004       MovDelay[x][y]--;
8005
8006       if (element == EL_ROBOT ||
8007           element == EL_YAMYAM ||
8008           element == EL_DARK_YAMYAM)
8009       {
8010         DrawLevelElementAnimationIfNeeded(x, y, element);
8011         PlayLevelSoundAction(x, y, ACTION_WAITING);
8012       }
8013       else if (element == EL_SP_ELECTRON)
8014         DrawLevelElementAnimationIfNeeded(x, y, element);
8015       else if (element == EL_DRAGON)
8016       {
8017         int i;
8018         int dir = MovDir[x][y];
8019         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8020         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8021         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8022                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8023                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8024                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8025         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8026
8027         GfxAction[x][y] = ACTION_ATTACKING;
8028
8029         if (IS_PLAYER(x, y))
8030           DrawPlayerField(x, y);
8031         else
8032           TEST_DrawLevelField(x, y);
8033
8034         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8035
8036         for (i = 1; i <= 3; i++)
8037         {
8038           int xx = x + i * dx;
8039           int yy = y + i * dy;
8040           int sx = SCREENX(xx);
8041           int sy = SCREENY(yy);
8042           int flame_graphic = graphic + (i - 1);
8043
8044           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8045             break;
8046
8047           if (MovDelay[x][y])
8048           {
8049             int flamed = MovingOrBlocked2Element(xx, yy);
8050
8051             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8052               Bang(xx, yy);
8053             else
8054               RemoveMovingField(xx, yy);
8055
8056             ChangeDelay[xx][yy] = 0;
8057
8058             Tile[xx][yy] = EL_FLAMES;
8059
8060             if (IN_SCR_FIELD(sx, sy))
8061             {
8062               TEST_DrawLevelFieldCrumbled(xx, yy);
8063               DrawGraphic(sx, sy, flame_graphic, frame);
8064             }
8065           }
8066           else
8067           {
8068             if (Tile[xx][yy] == EL_FLAMES)
8069               Tile[xx][yy] = EL_EMPTY;
8070             TEST_DrawLevelField(xx, yy);
8071           }
8072         }
8073       }
8074
8075       if (MovDelay[x][y])       // element still has to wait some time
8076       {
8077         PlayLevelSoundAction(x, y, ACTION_WAITING);
8078
8079         return;
8080       }
8081     }
8082
8083     // now make next step
8084
8085     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8086
8087     if (DONT_COLLIDE_WITH(element) &&
8088         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8089         !PLAYER_ENEMY_PROTECTED(newx, newy))
8090     {
8091       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8092
8093       return;
8094     }
8095
8096     else if (CAN_MOVE_INTO_ACID(element) &&
8097              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8098              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8099              (MovDir[x][y] == MV_DOWN ||
8100               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8101     {
8102       SplashAcid(newx, newy);
8103       Store[x][y] = EL_ACID;
8104     }
8105     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8106     {
8107       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8108           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8109           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8110           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8111       {
8112         RemoveField(x, y);
8113         TEST_DrawLevelField(x, y);
8114
8115         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8116         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8117           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8118
8119         game.friends_still_needed--;
8120         if (!game.friends_still_needed &&
8121             !game.GameOver &&
8122             game.all_players_gone)
8123           LevelSolved();
8124
8125         return;
8126       }
8127       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8128       {
8129         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8130           TEST_DrawLevelField(newx, newy);
8131         else
8132           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8133       }
8134       else if (!IS_FREE(newx, newy))
8135       {
8136         GfxAction[x][y] = ACTION_WAITING;
8137
8138         if (IS_PLAYER(x, y))
8139           DrawPlayerField(x, y);
8140         else
8141           TEST_DrawLevelField(x, y);
8142
8143         return;
8144       }
8145     }
8146     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8147     {
8148       if (IS_FOOD_PIG(Tile[newx][newy]))
8149       {
8150         if (IS_MOVING(newx, newy))
8151           RemoveMovingField(newx, newy);
8152         else
8153         {
8154           Tile[newx][newy] = EL_EMPTY;
8155           TEST_DrawLevelField(newx, newy);
8156         }
8157
8158         PlayLevelSound(x, y, SND_PIG_DIGGING);
8159       }
8160       else if (!IS_FREE(newx, newy))
8161       {
8162         if (IS_PLAYER(x, y))
8163           DrawPlayerField(x, y);
8164         else
8165           TEST_DrawLevelField(x, y);
8166
8167         return;
8168       }
8169     }
8170     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8171     {
8172       if (Store[x][y] != EL_EMPTY)
8173       {
8174         boolean can_clone = FALSE;
8175         int xx, yy;
8176
8177         // check if element to clone is still there
8178         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8179         {
8180           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8181           {
8182             can_clone = TRUE;
8183
8184             break;
8185           }
8186         }
8187
8188         // cannot clone or target field not free anymore -- do not clone
8189         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8190           Store[x][y] = EL_EMPTY;
8191       }
8192
8193       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8194       {
8195         if (IS_MV_DIAGONAL(MovDir[x][y]))
8196         {
8197           int diagonal_move_dir = MovDir[x][y];
8198           int stored = Store[x][y];
8199           int change_delay = 8;
8200           int graphic;
8201
8202           // android is moving diagonally
8203
8204           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8205
8206           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8207           GfxElement[x][y] = EL_EMC_ANDROID;
8208           GfxAction[x][y] = ACTION_SHRINKING;
8209           GfxDir[x][y] = diagonal_move_dir;
8210           ChangeDelay[x][y] = change_delay;
8211
8212           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8213                                    GfxDir[x][y]);
8214
8215           DrawLevelGraphicAnimation(x, y, graphic);
8216           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8217
8218           if (Tile[newx][newy] == EL_ACID)
8219           {
8220             SplashAcid(newx, newy);
8221
8222             return;
8223           }
8224
8225           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8226
8227           Store[newx][newy] = EL_EMC_ANDROID;
8228           GfxElement[newx][newy] = EL_EMC_ANDROID;
8229           GfxAction[newx][newy] = ACTION_GROWING;
8230           GfxDir[newx][newy] = diagonal_move_dir;
8231           ChangeDelay[newx][newy] = change_delay;
8232
8233           graphic = el_act_dir2img(GfxElement[newx][newy],
8234                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8235
8236           DrawLevelGraphicAnimation(newx, newy, graphic);
8237           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8238
8239           return;
8240         }
8241         else
8242         {
8243           Tile[newx][newy] = EL_EMPTY;
8244           TEST_DrawLevelField(newx, newy);
8245
8246           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8247         }
8248       }
8249       else if (!IS_FREE(newx, newy))
8250       {
8251         return;
8252       }
8253     }
8254     else if (IS_CUSTOM_ELEMENT(element) &&
8255              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8256     {
8257       if (!DigFieldByCE(newx, newy, element))
8258         return;
8259
8260       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8261       {
8262         RunnerVisit[x][y] = FrameCounter;
8263         PlayerVisit[x][y] /= 8;         // expire player visit path
8264       }
8265     }
8266     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8267     {
8268       if (!IS_FREE(newx, newy))
8269       {
8270         if (IS_PLAYER(x, y))
8271           DrawPlayerField(x, y);
8272         else
8273           TEST_DrawLevelField(x, y);
8274
8275         return;
8276       }
8277       else
8278       {
8279         boolean wanna_flame = !RND(10);
8280         int dx = newx - x, dy = newy - y;
8281         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8282         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8283         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8284                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8285         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8286                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8287
8288         if ((wanna_flame ||
8289              IS_CLASSIC_ENEMY(element1) ||
8290              IS_CLASSIC_ENEMY(element2)) &&
8291             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8292             element1 != EL_FLAMES && element2 != EL_FLAMES)
8293         {
8294           ResetGfxAnimation(x, y);
8295           GfxAction[x][y] = ACTION_ATTACKING;
8296
8297           if (IS_PLAYER(x, y))
8298             DrawPlayerField(x, y);
8299           else
8300             TEST_DrawLevelField(x, y);
8301
8302           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8303
8304           MovDelay[x][y] = 50;
8305
8306           Tile[newx][newy] = EL_FLAMES;
8307           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8308             Tile[newx1][newy1] = EL_FLAMES;
8309           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8310             Tile[newx2][newy2] = EL_FLAMES;
8311
8312           return;
8313         }
8314       }
8315     }
8316     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8317              Tile[newx][newy] == EL_DIAMOND)
8318     {
8319       if (IS_MOVING(newx, newy))
8320         RemoveMovingField(newx, newy);
8321       else
8322       {
8323         Tile[newx][newy] = EL_EMPTY;
8324         TEST_DrawLevelField(newx, newy);
8325       }
8326
8327       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8328     }
8329     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8330              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8331     {
8332       if (AmoebaNr[newx][newy])
8333       {
8334         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8335         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8336             Tile[newx][newy] == EL_BD_AMOEBA)
8337           AmoebaCnt[AmoebaNr[newx][newy]]--;
8338       }
8339
8340       if (IS_MOVING(newx, newy))
8341       {
8342         RemoveMovingField(newx, newy);
8343       }
8344       else
8345       {
8346         Tile[newx][newy] = EL_EMPTY;
8347         TEST_DrawLevelField(newx, newy);
8348       }
8349
8350       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8351     }
8352     else if ((element == EL_PACMAN || element == EL_MOLE)
8353              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8354     {
8355       if (AmoebaNr[newx][newy])
8356       {
8357         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8358         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8359             Tile[newx][newy] == EL_BD_AMOEBA)
8360           AmoebaCnt[AmoebaNr[newx][newy]]--;
8361       }
8362
8363       if (element == EL_MOLE)
8364       {
8365         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8366         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8367
8368         ResetGfxAnimation(x, y);
8369         GfxAction[x][y] = ACTION_DIGGING;
8370         TEST_DrawLevelField(x, y);
8371
8372         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8373
8374         return;                         // wait for shrinking amoeba
8375       }
8376       else      // element == EL_PACMAN
8377       {
8378         Tile[newx][newy] = EL_EMPTY;
8379         TEST_DrawLevelField(newx, newy);
8380         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8381       }
8382     }
8383     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8384              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8385               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8386     {
8387       // wait for shrinking amoeba to completely disappear
8388       return;
8389     }
8390     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8391     {
8392       // object was running against a wall
8393
8394       TurnRound(x, y);
8395
8396       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8397         DrawLevelElementAnimation(x, y, element);
8398
8399       if (DONT_TOUCH(element))
8400         TestIfBadThingTouchesPlayer(x, y);
8401
8402       return;
8403     }
8404
8405     InitMovingField(x, y, MovDir[x][y]);
8406
8407     PlayLevelSoundAction(x, y, ACTION_MOVING);
8408   }
8409
8410   if (MovDir[x][y])
8411     ContinueMoving(x, y);
8412 }
8413
8414 void ContinueMoving(int x, int y)
8415 {
8416   int element = Tile[x][y];
8417   struct ElementInfo *ei = &element_info[element];
8418   int direction = MovDir[x][y];
8419   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8420   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8421   int newx = x + dx, newy = y + dy;
8422   int stored = Store[x][y];
8423   int stored_new = Store[newx][newy];
8424   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8425   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8426   boolean last_line = (newy == lev_fieldy - 1);
8427
8428   MovPos[x][y] += getElementMoveStepsize(x, y);
8429
8430   if (pushed_by_player) // special case: moving object pushed by player
8431     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8432
8433   if (ABS(MovPos[x][y]) < TILEX)
8434   {
8435     TEST_DrawLevelField(x, y);
8436
8437     return;     // element is still moving
8438   }
8439
8440   // element reached destination field
8441
8442   Tile[x][y] = EL_EMPTY;
8443   Tile[newx][newy] = element;
8444   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8445
8446   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8447   {
8448     element = Tile[newx][newy] = EL_ACID;
8449   }
8450   else if (element == EL_MOLE)
8451   {
8452     Tile[x][y] = EL_SAND;
8453
8454     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8455   }
8456   else if (element == EL_QUICKSAND_FILLING)
8457   {
8458     element = Tile[newx][newy] = get_next_element(element);
8459     Store[newx][newy] = Store[x][y];
8460   }
8461   else if (element == EL_QUICKSAND_EMPTYING)
8462   {
8463     Tile[x][y] = get_next_element(element);
8464     element = Tile[newx][newy] = Store[x][y];
8465   }
8466   else if (element == EL_QUICKSAND_FAST_FILLING)
8467   {
8468     element = Tile[newx][newy] = get_next_element(element);
8469     Store[newx][newy] = Store[x][y];
8470   }
8471   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8472   {
8473     Tile[x][y] = get_next_element(element);
8474     element = Tile[newx][newy] = Store[x][y];
8475   }
8476   else if (element == EL_MAGIC_WALL_FILLING)
8477   {
8478     element = Tile[newx][newy] = get_next_element(element);
8479     if (!game.magic_wall_active)
8480       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8481     Store[newx][newy] = Store[x][y];
8482   }
8483   else if (element == EL_MAGIC_WALL_EMPTYING)
8484   {
8485     Tile[x][y] = get_next_element(element);
8486     if (!game.magic_wall_active)
8487       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8488     element = Tile[newx][newy] = Store[x][y];
8489
8490     InitField(newx, newy, FALSE);
8491   }
8492   else if (element == EL_BD_MAGIC_WALL_FILLING)
8493   {
8494     element = Tile[newx][newy] = get_next_element(element);
8495     if (!game.magic_wall_active)
8496       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8497     Store[newx][newy] = Store[x][y];
8498   }
8499   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8500   {
8501     Tile[x][y] = get_next_element(element);
8502     if (!game.magic_wall_active)
8503       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8504     element = Tile[newx][newy] = Store[x][y];
8505
8506     InitField(newx, newy, FALSE);
8507   }
8508   else if (element == EL_DC_MAGIC_WALL_FILLING)
8509   {
8510     element = Tile[newx][newy] = get_next_element(element);
8511     if (!game.magic_wall_active)
8512       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8513     Store[newx][newy] = Store[x][y];
8514   }
8515   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8516   {
8517     Tile[x][y] = get_next_element(element);
8518     if (!game.magic_wall_active)
8519       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8520     element = Tile[newx][newy] = Store[x][y];
8521
8522     InitField(newx, newy, FALSE);
8523   }
8524   else if (element == EL_AMOEBA_DROPPING)
8525   {
8526     Tile[x][y] = get_next_element(element);
8527     element = Tile[newx][newy] = Store[x][y];
8528   }
8529   else if (element == EL_SOKOBAN_OBJECT)
8530   {
8531     if (Back[x][y])
8532       Tile[x][y] = Back[x][y];
8533
8534     if (Back[newx][newy])
8535       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8536
8537     Back[x][y] = Back[newx][newy] = 0;
8538   }
8539
8540   Store[x][y] = EL_EMPTY;
8541   MovPos[x][y] = 0;
8542   MovDir[x][y] = 0;
8543   MovDelay[x][y] = 0;
8544
8545   MovDelay[newx][newy] = 0;
8546
8547   if (CAN_CHANGE_OR_HAS_ACTION(element))
8548   {
8549     // copy element change control values to new field
8550     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8551     ChangePage[newx][newy]  = ChangePage[x][y];
8552     ChangeCount[newx][newy] = ChangeCount[x][y];
8553     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8554   }
8555
8556   CustomValue[newx][newy] = CustomValue[x][y];
8557
8558   ChangeDelay[x][y] = 0;
8559   ChangePage[x][y] = -1;
8560   ChangeCount[x][y] = 0;
8561   ChangeEvent[x][y] = -1;
8562
8563   CustomValue[x][y] = 0;
8564
8565   // copy animation control values to new field
8566   GfxFrame[newx][newy]  = GfxFrame[x][y];
8567   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8568   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8569   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8570
8571   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8572
8573   // some elements can leave other elements behind after moving
8574   if (ei->move_leave_element != EL_EMPTY &&
8575       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8576       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8577   {
8578     int move_leave_element = ei->move_leave_element;
8579
8580     // this makes it possible to leave the removed element again
8581     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8582       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8583
8584     Tile[x][y] = move_leave_element;
8585
8586     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8587       MovDir[x][y] = direction;
8588
8589     InitField(x, y, FALSE);
8590
8591     if (GFX_CRUMBLED(Tile[x][y]))
8592       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8593
8594     if (ELEM_IS_PLAYER(move_leave_element))
8595       RelocatePlayer(x, y, move_leave_element);
8596   }
8597
8598   // do this after checking for left-behind element
8599   ResetGfxAnimation(x, y);      // reset animation values for old field
8600
8601   if (!CAN_MOVE(element) ||
8602       (CAN_FALL(element) && direction == MV_DOWN &&
8603        (element == EL_SPRING ||
8604         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8605         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8606     GfxDir[x][y] = MovDir[newx][newy] = 0;
8607
8608   TEST_DrawLevelField(x, y);
8609   TEST_DrawLevelField(newx, newy);
8610
8611   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8612
8613   // prevent pushed element from moving on in pushed direction
8614   if (pushed_by_player && CAN_MOVE(element) &&
8615       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8616       !(element_info[element].move_pattern & direction))
8617     TurnRound(newx, newy);
8618
8619   // prevent elements on conveyor belt from moving on in last direction
8620   if (pushed_by_conveyor && CAN_FALL(element) &&
8621       direction & MV_HORIZONTAL)
8622     MovDir[newx][newy] = 0;
8623
8624   if (!pushed_by_player)
8625   {
8626     int nextx = newx + dx, nexty = newy + dy;
8627     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8628
8629     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8630
8631     if (CAN_FALL(element) && direction == MV_DOWN)
8632       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8633
8634     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8635       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8636
8637     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8638       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8639   }
8640
8641   if (DONT_TOUCH(element))      // object may be nasty to player or others
8642   {
8643     TestIfBadThingTouchesPlayer(newx, newy);
8644     TestIfBadThingTouchesFriend(newx, newy);
8645
8646     if (!IS_CUSTOM_ELEMENT(element))
8647       TestIfBadThingTouchesOtherBadThing(newx, newy);
8648   }
8649   else if (element == EL_PENGUIN)
8650     TestIfFriendTouchesBadThing(newx, newy);
8651
8652   if (DONT_GET_HIT_BY(element))
8653   {
8654     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8655   }
8656
8657   // give the player one last chance (one more frame) to move away
8658   if (CAN_FALL(element) && direction == MV_DOWN &&
8659       (last_line || (!IS_FREE(x, newy + 1) &&
8660                      (!IS_PLAYER(x, newy + 1) ||
8661                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8662     Impact(x, newy);
8663
8664   if (pushed_by_player && !game.use_change_when_pushing_bug)
8665   {
8666     int push_side = MV_DIR_OPPOSITE(direction);
8667     struct PlayerInfo *player = PLAYERINFO(x, y);
8668
8669     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8670                                player->index_bit, push_side);
8671     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8672                                         player->index_bit, push_side);
8673   }
8674
8675   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8676     MovDelay[newx][newy] = 1;
8677
8678   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8679
8680   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8681   TestIfElementHitsCustomElement(newx, newy, direction);
8682   TestIfPlayerTouchesCustomElement(newx, newy);
8683   TestIfElementTouchesCustomElement(newx, newy);
8684
8685   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8686       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8687     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8688                              MV_DIR_OPPOSITE(direction));
8689 }
8690
8691 int AmoebaNeighbourNr(int ax, int ay)
8692 {
8693   int i;
8694   int element = Tile[ax][ay];
8695   int group_nr = 0;
8696   static int xy[4][2] =
8697   {
8698     { 0, -1 },
8699     { -1, 0 },
8700     { +1, 0 },
8701     { 0, +1 }
8702   };
8703
8704   for (i = 0; i < NUM_DIRECTIONS; i++)
8705   {
8706     int x = ax + xy[i][0];
8707     int y = ay + xy[i][1];
8708
8709     if (!IN_LEV_FIELD(x, y))
8710       continue;
8711
8712     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8713       group_nr = AmoebaNr[x][y];
8714   }
8715
8716   return group_nr;
8717 }
8718
8719 static void AmoebaMerge(int ax, int ay)
8720 {
8721   int i, x, y, xx, yy;
8722   int new_group_nr = AmoebaNr[ax][ay];
8723   static int xy[4][2] =
8724   {
8725     { 0, -1 },
8726     { -1, 0 },
8727     { +1, 0 },
8728     { 0, +1 }
8729   };
8730
8731   if (new_group_nr == 0)
8732     return;
8733
8734   for (i = 0; i < NUM_DIRECTIONS; i++)
8735   {
8736     x = ax + xy[i][0];
8737     y = ay + xy[i][1];
8738
8739     if (!IN_LEV_FIELD(x, y))
8740       continue;
8741
8742     if ((Tile[x][y] == EL_AMOEBA_FULL ||
8743          Tile[x][y] == EL_BD_AMOEBA ||
8744          Tile[x][y] == EL_AMOEBA_DEAD) &&
8745         AmoebaNr[x][y] != new_group_nr)
8746     {
8747       int old_group_nr = AmoebaNr[x][y];
8748
8749       if (old_group_nr == 0)
8750         return;
8751
8752       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8753       AmoebaCnt[old_group_nr] = 0;
8754       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8755       AmoebaCnt2[old_group_nr] = 0;
8756
8757       SCAN_PLAYFIELD(xx, yy)
8758       {
8759         if (AmoebaNr[xx][yy] == old_group_nr)
8760           AmoebaNr[xx][yy] = new_group_nr;
8761       }
8762     }
8763   }
8764 }
8765
8766 void AmoebaToDiamond(int ax, int ay)
8767 {
8768   int i, x, y;
8769
8770   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
8771   {
8772     int group_nr = AmoebaNr[ax][ay];
8773
8774 #ifdef DEBUG
8775     if (group_nr == 0)
8776     {
8777       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
8778       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
8779
8780       return;
8781     }
8782 #endif
8783
8784     SCAN_PLAYFIELD(x, y)
8785     {
8786       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8787       {
8788         AmoebaNr[x][y] = 0;
8789         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
8790       }
8791     }
8792
8793     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8794                             SND_AMOEBA_TURNING_TO_GEM :
8795                             SND_AMOEBA_TURNING_TO_ROCK));
8796     Bang(ax, ay);
8797   }
8798   else
8799   {
8800     static int xy[4][2] =
8801     {
8802       { 0, -1 },
8803       { -1, 0 },
8804       { +1, 0 },
8805       { 0, +1 }
8806     };
8807
8808     for (i = 0; i < NUM_DIRECTIONS; i++)
8809     {
8810       x = ax + xy[i][0];
8811       y = ay + xy[i][1];
8812
8813       if (!IN_LEV_FIELD(x, y))
8814         continue;
8815
8816       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
8817       {
8818         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8819                               SND_AMOEBA_TURNING_TO_GEM :
8820                               SND_AMOEBA_TURNING_TO_ROCK));
8821         Bang(x, y);
8822       }
8823     }
8824   }
8825 }
8826
8827 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
8828 {
8829   int x, y;
8830   int group_nr = AmoebaNr[ax][ay];
8831   boolean done = FALSE;
8832
8833 #ifdef DEBUG
8834   if (group_nr == 0)
8835   {
8836     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
8837     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
8838
8839     return;
8840   }
8841 #endif
8842
8843   SCAN_PLAYFIELD(x, y)
8844   {
8845     if (AmoebaNr[x][y] == group_nr &&
8846         (Tile[x][y] == EL_AMOEBA_DEAD ||
8847          Tile[x][y] == EL_BD_AMOEBA ||
8848          Tile[x][y] == EL_AMOEBA_GROWING))
8849     {
8850       AmoebaNr[x][y] = 0;
8851       Tile[x][y] = new_element;
8852       InitField(x, y, FALSE);
8853       TEST_DrawLevelField(x, y);
8854       done = TRUE;
8855     }
8856   }
8857
8858   if (done)
8859     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8860                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8861                             SND_BD_AMOEBA_TURNING_TO_GEM));
8862 }
8863
8864 static void AmoebaGrowing(int x, int y)
8865 {
8866   static unsigned int sound_delay = 0;
8867   static unsigned int sound_delay_value = 0;
8868
8869   if (!MovDelay[x][y])          // start new growing cycle
8870   {
8871     MovDelay[x][y] = 7;
8872
8873     if (DelayReached(&sound_delay, sound_delay_value))
8874     {
8875       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8876       sound_delay_value = 30;
8877     }
8878   }
8879
8880   if (MovDelay[x][y])           // wait some time before growing bigger
8881   {
8882     MovDelay[x][y]--;
8883     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8884     {
8885       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8886                                            6 - MovDelay[x][y]);
8887
8888       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8889     }
8890
8891     if (!MovDelay[x][y])
8892     {
8893       Tile[x][y] = Store[x][y];
8894       Store[x][y] = 0;
8895       TEST_DrawLevelField(x, y);
8896     }
8897   }
8898 }
8899
8900 static void AmoebaShrinking(int x, int y)
8901 {
8902   static unsigned int sound_delay = 0;
8903   static unsigned int sound_delay_value = 0;
8904
8905   if (!MovDelay[x][y])          // start new shrinking cycle
8906   {
8907     MovDelay[x][y] = 7;
8908
8909     if (DelayReached(&sound_delay, sound_delay_value))
8910       sound_delay_value = 30;
8911   }
8912
8913   if (MovDelay[x][y])           // wait some time before shrinking
8914   {
8915     MovDelay[x][y]--;
8916     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8917     {
8918       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8919                                            6 - MovDelay[x][y]);
8920
8921       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8922     }
8923
8924     if (!MovDelay[x][y])
8925     {
8926       Tile[x][y] = EL_EMPTY;
8927       TEST_DrawLevelField(x, y);
8928
8929       // don't let mole enter this field in this cycle;
8930       // (give priority to objects falling to this field from above)
8931       Stop[x][y] = TRUE;
8932     }
8933   }
8934 }
8935
8936 static void AmoebaReproduce(int ax, int ay)
8937 {
8938   int i;
8939   int element = Tile[ax][ay];
8940   int graphic = el2img(element);
8941   int newax = ax, neway = ay;
8942   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8943   static int xy[4][2] =
8944   {
8945     { 0, -1 },
8946     { -1, 0 },
8947     { +1, 0 },
8948     { 0, +1 }
8949   };
8950
8951   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8952   {
8953     Tile[ax][ay] = EL_AMOEBA_DEAD;
8954     TEST_DrawLevelField(ax, ay);
8955     return;
8956   }
8957
8958   if (IS_ANIMATED(graphic))
8959     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8960
8961   if (!MovDelay[ax][ay])        // start making new amoeba field
8962     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8963
8964   if (MovDelay[ax][ay])         // wait some time before making new amoeba
8965   {
8966     MovDelay[ax][ay]--;
8967     if (MovDelay[ax][ay])
8968       return;
8969   }
8970
8971   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
8972   {
8973     int start = RND(4);
8974     int x = ax + xy[start][0];
8975     int y = ay + xy[start][1];
8976
8977     if (!IN_LEV_FIELD(x, y))
8978       return;
8979
8980     if (IS_FREE(x, y) ||
8981         CAN_GROW_INTO(Tile[x][y]) ||
8982         Tile[x][y] == EL_QUICKSAND_EMPTY ||
8983         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
8984     {
8985       newax = x;
8986       neway = y;
8987     }
8988
8989     if (newax == ax && neway == ay)
8990       return;
8991   }
8992   else                          // normal or "filled" (BD style) amoeba
8993   {
8994     int start = RND(4);
8995     boolean waiting_for_player = FALSE;
8996
8997     for (i = 0; i < NUM_DIRECTIONS; i++)
8998     {
8999       int j = (start + i) % 4;
9000       int x = ax + xy[j][0];
9001       int y = ay + xy[j][1];
9002
9003       if (!IN_LEV_FIELD(x, y))
9004         continue;
9005
9006       if (IS_FREE(x, y) ||
9007           CAN_GROW_INTO(Tile[x][y]) ||
9008           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9009           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9010       {
9011         newax = x;
9012         neway = y;
9013         break;
9014       }
9015       else if (IS_PLAYER(x, y))
9016         waiting_for_player = TRUE;
9017     }
9018
9019     if (newax == ax && neway == ay)             // amoeba cannot grow
9020     {
9021       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9022       {
9023         Tile[ax][ay] = EL_AMOEBA_DEAD;
9024         TEST_DrawLevelField(ax, ay);
9025         AmoebaCnt[AmoebaNr[ax][ay]]--;
9026
9027         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9028         {
9029           if (element == EL_AMOEBA_FULL)
9030             AmoebaToDiamond(ax, ay);
9031           else if (element == EL_BD_AMOEBA)
9032             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9033         }
9034       }
9035       return;
9036     }
9037     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9038     {
9039       // amoeba gets larger by growing in some direction
9040
9041       int new_group_nr = AmoebaNr[ax][ay];
9042
9043 #ifdef DEBUG
9044   if (new_group_nr == 0)
9045   {
9046     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9047           newax, neway);
9048     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9049
9050     return;
9051   }
9052 #endif
9053
9054       AmoebaNr[newax][neway] = new_group_nr;
9055       AmoebaCnt[new_group_nr]++;
9056       AmoebaCnt2[new_group_nr]++;
9057
9058       // if amoeba touches other amoeba(s) after growing, unify them
9059       AmoebaMerge(newax, neway);
9060
9061       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9062       {
9063         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9064         return;
9065       }
9066     }
9067   }
9068
9069   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9070       (neway == lev_fieldy - 1 && newax != ax))
9071   {
9072     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9073     Store[newax][neway] = element;
9074   }
9075   else if (neway == ay || element == EL_EMC_DRIPPER)
9076   {
9077     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9078
9079     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9080   }
9081   else
9082   {
9083     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9084     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9085     Store[ax][ay] = EL_AMOEBA_DROP;
9086     ContinueMoving(ax, ay);
9087     return;
9088   }
9089
9090   TEST_DrawLevelField(newax, neway);
9091 }
9092
9093 static void Life(int ax, int ay)
9094 {
9095   int x1, y1, x2, y2;
9096   int life_time = 40;
9097   int element = Tile[ax][ay];
9098   int graphic = el2img(element);
9099   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9100                          level.biomaze);
9101   boolean changed = FALSE;
9102
9103   if (IS_ANIMATED(graphic))
9104     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9105
9106   if (Stop[ax][ay])
9107     return;
9108
9109   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9110     MovDelay[ax][ay] = life_time;
9111
9112   if (MovDelay[ax][ay])         // wait some time before next cycle
9113   {
9114     MovDelay[ax][ay]--;
9115     if (MovDelay[ax][ay])
9116       return;
9117   }
9118
9119   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9120   {
9121     int xx = ax+x1, yy = ay+y1;
9122     int old_element = Tile[xx][yy];
9123     int num_neighbours = 0;
9124
9125     if (!IN_LEV_FIELD(xx, yy))
9126       continue;
9127
9128     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9129     {
9130       int x = xx+x2, y = yy+y2;
9131
9132       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9133         continue;
9134
9135       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9136       boolean is_neighbour = FALSE;
9137
9138       if (level.use_life_bugs)
9139         is_neighbour =
9140           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9141            (IS_FREE(x, y)                             &&  Stop[x][y]));
9142       else
9143         is_neighbour =
9144           (Last[x][y] == element || is_player_cell);
9145
9146       if (is_neighbour)
9147         num_neighbours++;
9148     }
9149
9150     boolean is_free = FALSE;
9151
9152     if (level.use_life_bugs)
9153       is_free = (IS_FREE(xx, yy));
9154     else
9155       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9156
9157     if (xx == ax && yy == ay)           // field in the middle
9158     {
9159       if (num_neighbours < life_parameter[0] ||
9160           num_neighbours > life_parameter[1])
9161       {
9162         Tile[xx][yy] = EL_EMPTY;
9163         if (Tile[xx][yy] != old_element)
9164           TEST_DrawLevelField(xx, yy);
9165         Stop[xx][yy] = TRUE;
9166         changed = TRUE;
9167       }
9168     }
9169     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9170     {                                   // free border field
9171       if (num_neighbours >= life_parameter[2] &&
9172           num_neighbours <= life_parameter[3])
9173       {
9174         Tile[xx][yy] = element;
9175         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9176         if (Tile[xx][yy] != old_element)
9177           TEST_DrawLevelField(xx, yy);
9178         Stop[xx][yy] = TRUE;
9179         changed = TRUE;
9180       }
9181     }
9182   }
9183
9184   if (changed)
9185     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9186                    SND_GAME_OF_LIFE_GROWING);
9187 }
9188
9189 static void InitRobotWheel(int x, int y)
9190 {
9191   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9192 }
9193
9194 static void RunRobotWheel(int x, int y)
9195 {
9196   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9197 }
9198
9199 static void StopRobotWheel(int x, int y)
9200 {
9201   if (game.robot_wheel_x == x &&
9202       game.robot_wheel_y == y)
9203   {
9204     game.robot_wheel_x = -1;
9205     game.robot_wheel_y = -1;
9206     game.robot_wheel_active = FALSE;
9207   }
9208 }
9209
9210 static void InitTimegateWheel(int x, int y)
9211 {
9212   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9213 }
9214
9215 static void RunTimegateWheel(int x, int y)
9216 {
9217   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9218 }
9219
9220 static void InitMagicBallDelay(int x, int y)
9221 {
9222   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9223 }
9224
9225 static void ActivateMagicBall(int bx, int by)
9226 {
9227   int x, y;
9228
9229   if (level.ball_random)
9230   {
9231     int pos_border = RND(8);    // select one of the eight border elements
9232     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9233     int xx = pos_content % 3;
9234     int yy = pos_content / 3;
9235
9236     x = bx - 1 + xx;
9237     y = by - 1 + yy;
9238
9239     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9240       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9241   }
9242   else
9243   {
9244     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9245     {
9246       int xx = x - bx + 1;
9247       int yy = y - by + 1;
9248
9249       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9250         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9251     }
9252   }
9253
9254   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9255 }
9256
9257 static void CheckExit(int x, int y)
9258 {
9259   if (game.gems_still_needed > 0 ||
9260       game.sokoban_fields_still_needed > 0 ||
9261       game.sokoban_objects_still_needed > 0 ||
9262       game.lights_still_needed > 0)
9263   {
9264     int element = Tile[x][y];
9265     int graphic = el2img(element);
9266
9267     if (IS_ANIMATED(graphic))
9268       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9269
9270     return;
9271   }
9272
9273   // do not re-open exit door closed after last player
9274   if (game.all_players_gone)
9275     return;
9276
9277   Tile[x][y] = EL_EXIT_OPENING;
9278
9279   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9280 }
9281
9282 static void CheckExitEM(int x, int y)
9283 {
9284   if (game.gems_still_needed > 0 ||
9285       game.sokoban_fields_still_needed > 0 ||
9286       game.sokoban_objects_still_needed > 0 ||
9287       game.lights_still_needed > 0)
9288   {
9289     int element = Tile[x][y];
9290     int graphic = el2img(element);
9291
9292     if (IS_ANIMATED(graphic))
9293       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9294
9295     return;
9296   }
9297
9298   // do not re-open exit door closed after last player
9299   if (game.all_players_gone)
9300     return;
9301
9302   Tile[x][y] = EL_EM_EXIT_OPENING;
9303
9304   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9305 }
9306
9307 static void CheckExitSteel(int x, int y)
9308 {
9309   if (game.gems_still_needed > 0 ||
9310       game.sokoban_fields_still_needed > 0 ||
9311       game.sokoban_objects_still_needed > 0 ||
9312       game.lights_still_needed > 0)
9313   {
9314     int element = Tile[x][y];
9315     int graphic = el2img(element);
9316
9317     if (IS_ANIMATED(graphic))
9318       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9319
9320     return;
9321   }
9322
9323   // do not re-open exit door closed after last player
9324   if (game.all_players_gone)
9325     return;
9326
9327   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9328
9329   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9330 }
9331
9332 static void CheckExitSteelEM(int x, int y)
9333 {
9334   if (game.gems_still_needed > 0 ||
9335       game.sokoban_fields_still_needed > 0 ||
9336       game.sokoban_objects_still_needed > 0 ||
9337       game.lights_still_needed > 0)
9338   {
9339     int element = Tile[x][y];
9340     int graphic = el2img(element);
9341
9342     if (IS_ANIMATED(graphic))
9343       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9344
9345     return;
9346   }
9347
9348   // do not re-open exit door closed after last player
9349   if (game.all_players_gone)
9350     return;
9351
9352   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9353
9354   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9355 }
9356
9357 static void CheckExitSP(int x, int y)
9358 {
9359   if (game.gems_still_needed > 0)
9360   {
9361     int element = Tile[x][y];
9362     int graphic = el2img(element);
9363
9364     if (IS_ANIMATED(graphic))
9365       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9366
9367     return;
9368   }
9369
9370   // do not re-open exit door closed after last player
9371   if (game.all_players_gone)
9372     return;
9373
9374   Tile[x][y] = EL_SP_EXIT_OPENING;
9375
9376   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9377 }
9378
9379 static void CloseAllOpenTimegates(void)
9380 {
9381   int x, y;
9382
9383   SCAN_PLAYFIELD(x, y)
9384   {
9385     int element = Tile[x][y];
9386
9387     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9388     {
9389       Tile[x][y] = EL_TIMEGATE_CLOSING;
9390
9391       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9392     }
9393   }
9394 }
9395
9396 static void DrawTwinkleOnField(int x, int y)
9397 {
9398   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9399     return;
9400
9401   if (Tile[x][y] == EL_BD_DIAMOND)
9402     return;
9403
9404   if (MovDelay[x][y] == 0)      // next animation frame
9405     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9406
9407   if (MovDelay[x][y] != 0)      // wait some time before next frame
9408   {
9409     MovDelay[x][y]--;
9410
9411     DrawLevelElementAnimation(x, y, Tile[x][y]);
9412
9413     if (MovDelay[x][y] != 0)
9414     {
9415       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9416                                            10 - MovDelay[x][y]);
9417
9418       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9419     }
9420   }
9421 }
9422
9423 static void MauerWaechst(int x, int y)
9424 {
9425   int delay = 6;
9426
9427   if (!MovDelay[x][y])          // next animation frame
9428     MovDelay[x][y] = 3 * delay;
9429
9430   if (MovDelay[x][y])           // wait some time before next frame
9431   {
9432     MovDelay[x][y]--;
9433
9434     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9435     {
9436       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9437       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9438
9439       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9440     }
9441
9442     if (!MovDelay[x][y])
9443     {
9444       if (MovDir[x][y] == MV_LEFT)
9445       {
9446         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9447           TEST_DrawLevelField(x - 1, y);
9448       }
9449       else if (MovDir[x][y] == MV_RIGHT)
9450       {
9451         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9452           TEST_DrawLevelField(x + 1, y);
9453       }
9454       else if (MovDir[x][y] == MV_UP)
9455       {
9456         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9457           TEST_DrawLevelField(x, y - 1);
9458       }
9459       else
9460       {
9461         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9462           TEST_DrawLevelField(x, y + 1);
9463       }
9464
9465       Tile[x][y] = Store[x][y];
9466       Store[x][y] = 0;
9467       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9468       TEST_DrawLevelField(x, y);
9469     }
9470   }
9471 }
9472
9473 static void MauerAbleger(int ax, int ay)
9474 {
9475   int element = Tile[ax][ay];
9476   int graphic = el2img(element);
9477   boolean oben_frei = FALSE, unten_frei = FALSE;
9478   boolean links_frei = FALSE, rechts_frei = FALSE;
9479   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9480   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9481   boolean new_wall = FALSE;
9482
9483   if (IS_ANIMATED(graphic))
9484     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9485
9486   if (!MovDelay[ax][ay])        // start building new wall
9487     MovDelay[ax][ay] = 6;
9488
9489   if (MovDelay[ax][ay])         // wait some time before building new wall
9490   {
9491     MovDelay[ax][ay]--;
9492     if (MovDelay[ax][ay])
9493       return;
9494   }
9495
9496   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9497     oben_frei = TRUE;
9498   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9499     unten_frei = TRUE;
9500   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9501     links_frei = TRUE;
9502   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9503     rechts_frei = TRUE;
9504
9505   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9506       element == EL_EXPANDABLE_WALL_ANY)
9507   {
9508     if (oben_frei)
9509     {
9510       Tile[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9511       Store[ax][ay-1] = element;
9512       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9513       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9514         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9515                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9516       new_wall = TRUE;
9517     }
9518     if (unten_frei)
9519     {
9520       Tile[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9521       Store[ax][ay+1] = element;
9522       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9523       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9524         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9525                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9526       new_wall = TRUE;
9527     }
9528   }
9529
9530   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9531       element == EL_EXPANDABLE_WALL_ANY ||
9532       element == EL_EXPANDABLE_WALL ||
9533       element == EL_BD_EXPANDABLE_WALL)
9534   {
9535     if (links_frei)
9536     {
9537       Tile[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9538       Store[ax-1][ay] = element;
9539       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9540       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9541         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9542                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9543       new_wall = TRUE;
9544     }
9545
9546     if (rechts_frei)
9547     {
9548       Tile[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9549       Store[ax+1][ay] = element;
9550       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9551       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9552         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9553                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9554       new_wall = TRUE;
9555     }
9556   }
9557
9558   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9559     TEST_DrawLevelField(ax, ay);
9560
9561   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9562     oben_massiv = TRUE;
9563   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9564     unten_massiv = TRUE;
9565   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9566     links_massiv = TRUE;
9567   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9568     rechts_massiv = TRUE;
9569
9570   if (((oben_massiv && unten_massiv) ||
9571        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9572        element == EL_EXPANDABLE_WALL) &&
9573       ((links_massiv && rechts_massiv) ||
9574        element == EL_EXPANDABLE_WALL_VERTICAL))
9575     Tile[ax][ay] = EL_WALL;
9576
9577   if (new_wall)
9578     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9579 }
9580
9581 static void MauerAblegerStahl(int ax, int ay)
9582 {
9583   int element = Tile[ax][ay];
9584   int graphic = el2img(element);
9585   boolean oben_frei = FALSE, unten_frei = FALSE;
9586   boolean links_frei = FALSE, rechts_frei = FALSE;
9587   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9588   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9589   boolean new_wall = FALSE;
9590
9591   if (IS_ANIMATED(graphic))
9592     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9593
9594   if (!MovDelay[ax][ay])        // start building new wall
9595     MovDelay[ax][ay] = 6;
9596
9597   if (MovDelay[ax][ay])         // wait some time before building new wall
9598   {
9599     MovDelay[ax][ay]--;
9600     if (MovDelay[ax][ay])
9601       return;
9602   }
9603
9604   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9605     oben_frei = TRUE;
9606   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9607     unten_frei = TRUE;
9608   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9609     links_frei = TRUE;
9610   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9611     rechts_frei = TRUE;
9612
9613   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9614       element == EL_EXPANDABLE_STEELWALL_ANY)
9615   {
9616     if (oben_frei)
9617     {
9618       Tile[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9619       Store[ax][ay-1] = element;
9620       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9621       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9622         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9623                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9624       new_wall = TRUE;
9625     }
9626     if (unten_frei)
9627     {
9628       Tile[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9629       Store[ax][ay+1] = element;
9630       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9631       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9632         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9633                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9634       new_wall = TRUE;
9635     }
9636   }
9637
9638   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9639       element == EL_EXPANDABLE_STEELWALL_ANY)
9640   {
9641     if (links_frei)
9642     {
9643       Tile[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9644       Store[ax-1][ay] = element;
9645       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9646       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9647         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9648                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9649       new_wall = TRUE;
9650     }
9651
9652     if (rechts_frei)
9653     {
9654       Tile[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9655       Store[ax+1][ay] = element;
9656       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9657       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9658         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9659                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9660       new_wall = TRUE;
9661     }
9662   }
9663
9664   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9665     oben_massiv = TRUE;
9666   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9667     unten_massiv = TRUE;
9668   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9669     links_massiv = TRUE;
9670   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9671     rechts_massiv = TRUE;
9672
9673   if (((oben_massiv && unten_massiv) ||
9674        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9675       ((links_massiv && rechts_massiv) ||
9676        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9677     Tile[ax][ay] = EL_STEELWALL;
9678
9679   if (new_wall)
9680     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9681 }
9682
9683 static void CheckForDragon(int x, int y)
9684 {
9685   int i, j;
9686   boolean dragon_found = FALSE;
9687   static int xy[4][2] =
9688   {
9689     { 0, -1 },
9690     { -1, 0 },
9691     { +1, 0 },
9692     { 0, +1 }
9693   };
9694
9695   for (i = 0; i < NUM_DIRECTIONS; i++)
9696   {
9697     for (j = 0; j < 4; j++)
9698     {
9699       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9700
9701       if (IN_LEV_FIELD(xx, yy) &&
9702           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9703       {
9704         if (Tile[xx][yy] == EL_DRAGON)
9705           dragon_found = TRUE;
9706       }
9707       else
9708         break;
9709     }
9710   }
9711
9712   if (!dragon_found)
9713   {
9714     for (i = 0; i < NUM_DIRECTIONS; i++)
9715     {
9716       for (j = 0; j < 3; j++)
9717       {
9718         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9719   
9720         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9721         {
9722           Tile[xx][yy] = EL_EMPTY;
9723           TEST_DrawLevelField(xx, yy);
9724         }
9725         else
9726           break;
9727       }
9728     }
9729   }
9730 }
9731
9732 static void InitBuggyBase(int x, int y)
9733 {
9734   int element = Tile[x][y];
9735   int activating_delay = FRAMES_PER_SECOND / 4;
9736
9737   ChangeDelay[x][y] =
9738     (element == EL_SP_BUGGY_BASE ?
9739      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9740      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9741      activating_delay :
9742      element == EL_SP_BUGGY_BASE_ACTIVE ?
9743      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9744 }
9745
9746 static void WarnBuggyBase(int x, int y)
9747 {
9748   int i;
9749   static int xy[4][2] =
9750   {
9751     { 0, -1 },
9752     { -1, 0 },
9753     { +1, 0 },
9754     { 0, +1 }
9755   };
9756
9757   for (i = 0; i < NUM_DIRECTIONS; i++)
9758   {
9759     int xx = x + xy[i][0];
9760     int yy = y + xy[i][1];
9761
9762     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9763     {
9764       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9765
9766       break;
9767     }
9768   }
9769 }
9770
9771 static void InitTrap(int x, int y)
9772 {
9773   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9774 }
9775
9776 static void ActivateTrap(int x, int y)
9777 {
9778   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9779 }
9780
9781 static void ChangeActiveTrap(int x, int y)
9782 {
9783   int graphic = IMG_TRAP_ACTIVE;
9784
9785   // if new animation frame was drawn, correct crumbled sand border
9786   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9787     TEST_DrawLevelFieldCrumbled(x, y);
9788 }
9789
9790 static int getSpecialActionElement(int element, int number, int base_element)
9791 {
9792   return (element != EL_EMPTY ? element :
9793           number != -1 ? base_element + number - 1 :
9794           EL_EMPTY);
9795 }
9796
9797 static int getModifiedActionNumber(int value_old, int operator, int operand,
9798                                    int value_min, int value_max)
9799 {
9800   int value_new = (operator == CA_MODE_SET      ? operand :
9801                    operator == CA_MODE_ADD      ? value_old + operand :
9802                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9803                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9804                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9805                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9806                    value_old);
9807
9808   return (value_new < value_min ? value_min :
9809           value_new > value_max ? value_max :
9810           value_new);
9811 }
9812
9813 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9814 {
9815   struct ElementInfo *ei = &element_info[element];
9816   struct ElementChangeInfo *change = &ei->change_page[page];
9817   int target_element = change->target_element;
9818   int action_type = change->action_type;
9819   int action_mode = change->action_mode;
9820   int action_arg = change->action_arg;
9821   int action_element = change->action_element;
9822   int i;
9823
9824   if (!change->has_action)
9825     return;
9826
9827   // ---------- determine action paramater values -----------------------------
9828
9829   int level_time_value =
9830     (level.time > 0 ? TimeLeft :
9831      TimePlayed);
9832
9833   int action_arg_element_raw =
9834     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9835      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9836      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9837      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9838      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9839      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9840      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9841      EL_EMPTY);
9842   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9843
9844   int action_arg_direction =
9845     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9846      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9847      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9848      change->actual_trigger_side :
9849      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9850      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9851      MV_NONE);
9852
9853   int action_arg_number_min =
9854     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9855      CA_ARG_MIN);
9856
9857   int action_arg_number_max =
9858     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9859      action_type == CA_SET_LEVEL_GEMS ? 999 :
9860      action_type == CA_SET_LEVEL_TIME ? 9999 :
9861      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9862      action_type == CA_SET_CE_VALUE ? 9999 :
9863      action_type == CA_SET_CE_SCORE ? 9999 :
9864      CA_ARG_MAX);
9865
9866   int action_arg_number_reset =
9867     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9868      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9869      action_type == CA_SET_LEVEL_TIME ? level.time :
9870      action_type == CA_SET_LEVEL_SCORE ? 0 :
9871      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9872      action_type == CA_SET_CE_SCORE ? 0 :
9873      0);
9874
9875   int action_arg_number =
9876     (action_arg <= CA_ARG_MAX ? action_arg :
9877      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9878      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9879      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9880      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9881      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9882      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9883      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9884      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9885      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9886      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9887      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
9888      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
9889      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9890      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9891      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9892      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9893      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9894      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9895      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9896      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9897      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9898      -1);
9899
9900   int action_arg_number_old =
9901     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
9902      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9903      action_type == CA_SET_LEVEL_SCORE ? game.score :
9904      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9905      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9906      0);
9907
9908   int action_arg_number_new =
9909     getModifiedActionNumber(action_arg_number_old,
9910                             action_mode, action_arg_number,
9911                             action_arg_number_min, action_arg_number_max);
9912
9913   int trigger_player_bits =
9914     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9915      change->actual_trigger_player_bits : change->trigger_player);
9916
9917   int action_arg_player_bits =
9918     (action_arg >= CA_ARG_PLAYER_1 &&
9919      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9920      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9921      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9922      PLAYER_BITS_ANY);
9923
9924   // ---------- execute action  -----------------------------------------------
9925
9926   switch (action_type)
9927   {
9928     case CA_NO_ACTION:
9929     {
9930       return;
9931     }
9932
9933     // ---------- level actions  ----------------------------------------------
9934
9935     case CA_RESTART_LEVEL:
9936     {
9937       game.restart_level = TRUE;
9938
9939       break;
9940     }
9941
9942     case CA_SHOW_ENVELOPE:
9943     {
9944       int element = getSpecialActionElement(action_arg_element,
9945                                             action_arg_number, EL_ENVELOPE_1);
9946
9947       if (IS_ENVELOPE(element))
9948         local_player->show_envelope = element;
9949
9950       break;
9951     }
9952
9953     case CA_SET_LEVEL_TIME:
9954     {
9955       if (level.time > 0)       // only modify limited time value
9956       {
9957         TimeLeft = action_arg_number_new;
9958
9959         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9960
9961         DisplayGameControlValues();
9962
9963         if (!TimeLeft && setup.time_limit)
9964           for (i = 0; i < MAX_PLAYERS; i++)
9965             KillPlayer(&stored_player[i]);
9966       }
9967
9968       break;
9969     }
9970
9971     case CA_SET_LEVEL_SCORE:
9972     {
9973       game.score = action_arg_number_new;
9974
9975       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
9976
9977       DisplayGameControlValues();
9978
9979       break;
9980     }
9981
9982     case CA_SET_LEVEL_GEMS:
9983     {
9984       game.gems_still_needed = action_arg_number_new;
9985
9986       game.snapshot.collected_item = TRUE;
9987
9988       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
9989
9990       DisplayGameControlValues();
9991
9992       break;
9993     }
9994
9995     case CA_SET_LEVEL_WIND:
9996     {
9997       game.wind_direction = action_arg_direction;
9998
9999       break;
10000     }
10001
10002     case CA_SET_LEVEL_RANDOM_SEED:
10003     {
10004       // ensure that setting a new random seed while playing is predictable
10005       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10006
10007       break;
10008     }
10009
10010     // ---------- player actions  ---------------------------------------------
10011
10012     case CA_MOVE_PLAYER:
10013     case CA_MOVE_PLAYER_NEW:
10014     {
10015       // automatically move to the next field in specified direction
10016       for (i = 0; i < MAX_PLAYERS; i++)
10017         if (trigger_player_bits & (1 << i))
10018           if (action_type == CA_MOVE_PLAYER ||
10019               stored_player[i].MovPos == 0)
10020             stored_player[i].programmed_action = action_arg_direction;
10021
10022       break;
10023     }
10024
10025     case CA_EXIT_PLAYER:
10026     {
10027       for (i = 0; i < MAX_PLAYERS; i++)
10028         if (action_arg_player_bits & (1 << i))
10029           ExitPlayer(&stored_player[i]);
10030
10031       if (game.players_still_needed == 0)
10032         LevelSolved();
10033
10034       break;
10035     }
10036
10037     case CA_KILL_PLAYER:
10038     {
10039       for (i = 0; i < MAX_PLAYERS; i++)
10040         if (action_arg_player_bits & (1 << i))
10041           KillPlayer(&stored_player[i]);
10042
10043       break;
10044     }
10045
10046     case CA_SET_PLAYER_KEYS:
10047     {
10048       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10049       int element = getSpecialActionElement(action_arg_element,
10050                                             action_arg_number, EL_KEY_1);
10051
10052       if (IS_KEY(element))
10053       {
10054         for (i = 0; i < MAX_PLAYERS; i++)
10055         {
10056           if (trigger_player_bits & (1 << i))
10057           {
10058             stored_player[i].key[KEY_NR(element)] = key_state;
10059
10060             DrawGameDoorValues();
10061           }
10062         }
10063       }
10064
10065       break;
10066     }
10067
10068     case CA_SET_PLAYER_SPEED:
10069     {
10070       for (i = 0; i < MAX_PLAYERS; i++)
10071       {
10072         if (trigger_player_bits & (1 << i))
10073         {
10074           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10075
10076           if (action_arg == CA_ARG_SPEED_FASTER &&
10077               stored_player[i].cannot_move)
10078           {
10079             action_arg_number = STEPSIZE_VERY_SLOW;
10080           }
10081           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10082                    action_arg == CA_ARG_SPEED_FASTER)
10083           {
10084             action_arg_number = 2;
10085             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10086                            CA_MODE_MULTIPLY);
10087           }
10088           else if (action_arg == CA_ARG_NUMBER_RESET)
10089           {
10090             action_arg_number = level.initial_player_stepsize[i];
10091           }
10092
10093           move_stepsize =
10094             getModifiedActionNumber(move_stepsize,
10095                                     action_mode,
10096                                     action_arg_number,
10097                                     action_arg_number_min,
10098                                     action_arg_number_max);
10099
10100           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10101         }
10102       }
10103
10104       break;
10105     }
10106
10107     case CA_SET_PLAYER_SHIELD:
10108     {
10109       for (i = 0; i < MAX_PLAYERS; i++)
10110       {
10111         if (trigger_player_bits & (1 << i))
10112         {
10113           if (action_arg == CA_ARG_SHIELD_OFF)
10114           {
10115             stored_player[i].shield_normal_time_left = 0;
10116             stored_player[i].shield_deadly_time_left = 0;
10117           }
10118           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10119           {
10120             stored_player[i].shield_normal_time_left = 999999;
10121           }
10122           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10123           {
10124             stored_player[i].shield_normal_time_left = 999999;
10125             stored_player[i].shield_deadly_time_left = 999999;
10126           }
10127         }
10128       }
10129
10130       break;
10131     }
10132
10133     case CA_SET_PLAYER_GRAVITY:
10134     {
10135       for (i = 0; i < MAX_PLAYERS; i++)
10136       {
10137         if (trigger_player_bits & (1 << i))
10138         {
10139           stored_player[i].gravity =
10140             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10141              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10142              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10143              stored_player[i].gravity);
10144         }
10145       }
10146
10147       break;
10148     }
10149
10150     case CA_SET_PLAYER_ARTWORK:
10151     {
10152       for (i = 0; i < MAX_PLAYERS; i++)
10153       {
10154         if (trigger_player_bits & (1 << i))
10155         {
10156           int artwork_element = action_arg_element;
10157
10158           if (action_arg == CA_ARG_ELEMENT_RESET)
10159             artwork_element =
10160               (level.use_artwork_element[i] ? level.artwork_element[i] :
10161                stored_player[i].element_nr);
10162
10163           if (stored_player[i].artwork_element != artwork_element)
10164             stored_player[i].Frame = 0;
10165
10166           stored_player[i].artwork_element = artwork_element;
10167
10168           SetPlayerWaiting(&stored_player[i], FALSE);
10169
10170           // set number of special actions for bored and sleeping animation
10171           stored_player[i].num_special_action_bored =
10172             get_num_special_action(artwork_element,
10173                                    ACTION_BORING_1, ACTION_BORING_LAST);
10174           stored_player[i].num_special_action_sleeping =
10175             get_num_special_action(artwork_element,
10176                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10177         }
10178       }
10179
10180       break;
10181     }
10182
10183     case CA_SET_PLAYER_INVENTORY:
10184     {
10185       for (i = 0; i < MAX_PLAYERS; i++)
10186       {
10187         struct PlayerInfo *player = &stored_player[i];
10188         int j, k;
10189
10190         if (trigger_player_bits & (1 << i))
10191         {
10192           int inventory_element = action_arg_element;
10193
10194           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10195               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10196               action_arg == CA_ARG_ELEMENT_ACTION)
10197           {
10198             int element = inventory_element;
10199             int collect_count = element_info[element].collect_count_initial;
10200
10201             if (!IS_CUSTOM_ELEMENT(element))
10202               collect_count = 1;
10203
10204             if (collect_count == 0)
10205               player->inventory_infinite_element = element;
10206             else
10207               for (k = 0; k < collect_count; k++)
10208                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10209                   player->inventory_element[player->inventory_size++] =
10210                     element;
10211           }
10212           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10213                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10214                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10215           {
10216             if (player->inventory_infinite_element != EL_UNDEFINED &&
10217                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10218                                      action_arg_element_raw))
10219               player->inventory_infinite_element = EL_UNDEFINED;
10220
10221             for (k = 0, j = 0; j < player->inventory_size; j++)
10222             {
10223               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10224                                         action_arg_element_raw))
10225                 player->inventory_element[k++] = player->inventory_element[j];
10226             }
10227
10228             player->inventory_size = k;
10229           }
10230           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10231           {
10232             if (player->inventory_size > 0)
10233             {
10234               for (j = 0; j < player->inventory_size - 1; j++)
10235                 player->inventory_element[j] = player->inventory_element[j + 1];
10236
10237               player->inventory_size--;
10238             }
10239           }
10240           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10241           {
10242             if (player->inventory_size > 0)
10243               player->inventory_size--;
10244           }
10245           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10246           {
10247             player->inventory_infinite_element = EL_UNDEFINED;
10248             player->inventory_size = 0;
10249           }
10250           else if (action_arg == CA_ARG_INVENTORY_RESET)
10251           {
10252             player->inventory_infinite_element = EL_UNDEFINED;
10253             player->inventory_size = 0;
10254
10255             if (level.use_initial_inventory[i])
10256             {
10257               for (j = 0; j < level.initial_inventory_size[i]; j++)
10258               {
10259                 int element = level.initial_inventory_content[i][j];
10260                 int collect_count = element_info[element].collect_count_initial;
10261
10262                 if (!IS_CUSTOM_ELEMENT(element))
10263                   collect_count = 1;
10264
10265                 if (collect_count == 0)
10266                   player->inventory_infinite_element = element;
10267                 else
10268                   for (k = 0; k < collect_count; k++)
10269                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10270                       player->inventory_element[player->inventory_size++] =
10271                         element;
10272               }
10273             }
10274           }
10275         }
10276       }
10277
10278       break;
10279     }
10280
10281     // ---------- CE actions  -------------------------------------------------
10282
10283     case CA_SET_CE_VALUE:
10284     {
10285       int last_ce_value = CustomValue[x][y];
10286
10287       CustomValue[x][y] = action_arg_number_new;
10288
10289       if (CustomValue[x][y] != last_ce_value)
10290       {
10291         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10292         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10293
10294         if (CustomValue[x][y] == 0)
10295         {
10296           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10297           ChangeCount[x][y] = 0;        // allow at least one more change
10298
10299           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10300           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10301         }
10302       }
10303
10304       break;
10305     }
10306
10307     case CA_SET_CE_SCORE:
10308     {
10309       int last_ce_score = ei->collect_score;
10310
10311       ei->collect_score = action_arg_number_new;
10312
10313       if (ei->collect_score != last_ce_score)
10314       {
10315         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10316         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10317
10318         if (ei->collect_score == 0)
10319         {
10320           int xx, yy;
10321
10322           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10323           ChangeCount[x][y] = 0;        // allow at least one more change
10324
10325           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10326           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10327
10328           /*
10329             This is a very special case that seems to be a mixture between
10330             CheckElementChange() and CheckTriggeredElementChange(): while
10331             the first one only affects single elements that are triggered
10332             directly, the second one affects multiple elements in the playfield
10333             that are triggered indirectly by another element. This is a third
10334             case: Changing the CE score always affects multiple identical CEs,
10335             so every affected CE must be checked, not only the single CE for
10336             which the CE score was changed in the first place (as every instance
10337             of that CE shares the same CE score, and therefore also can change)!
10338           */
10339           SCAN_PLAYFIELD(xx, yy)
10340           {
10341             if (Tile[xx][yy] == element)
10342               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10343                                  CE_SCORE_GETS_ZERO);
10344           }
10345         }
10346       }
10347
10348       break;
10349     }
10350
10351     case CA_SET_CE_ARTWORK:
10352     {
10353       int artwork_element = action_arg_element;
10354       boolean reset_frame = FALSE;
10355       int xx, yy;
10356
10357       if (action_arg == CA_ARG_ELEMENT_RESET)
10358         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10359                            element);
10360
10361       if (ei->gfx_element != artwork_element)
10362         reset_frame = TRUE;
10363
10364       ei->gfx_element = artwork_element;
10365
10366       SCAN_PLAYFIELD(xx, yy)
10367       {
10368         if (Tile[xx][yy] == element)
10369         {
10370           if (reset_frame)
10371           {
10372             ResetGfxAnimation(xx, yy);
10373             ResetRandomAnimationValue(xx, yy);
10374           }
10375
10376           TEST_DrawLevelField(xx, yy);
10377         }
10378       }
10379
10380       break;
10381     }
10382
10383     // ---------- engine actions  ---------------------------------------------
10384
10385     case CA_SET_ENGINE_SCAN_MODE:
10386     {
10387       InitPlayfieldScanMode(action_arg);
10388
10389       break;
10390     }
10391
10392     default:
10393       break;
10394   }
10395 }
10396
10397 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10398 {
10399   int old_element = Tile[x][y];
10400   int new_element = GetElementFromGroupElement(element);
10401   int previous_move_direction = MovDir[x][y];
10402   int last_ce_value = CustomValue[x][y];
10403   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10404   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10405   boolean add_player_onto_element = (new_element_is_player &&
10406                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10407                                      IS_WALKABLE(old_element));
10408
10409   if (!add_player_onto_element)
10410   {
10411     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10412       RemoveMovingField(x, y);
10413     else
10414       RemoveField(x, y);
10415
10416     Tile[x][y] = new_element;
10417
10418     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10419       MovDir[x][y] = previous_move_direction;
10420
10421     if (element_info[new_element].use_last_ce_value)
10422       CustomValue[x][y] = last_ce_value;
10423
10424     InitField_WithBug1(x, y, FALSE);
10425
10426     new_element = Tile[x][y];   // element may have changed
10427
10428     ResetGfxAnimation(x, y);
10429     ResetRandomAnimationValue(x, y);
10430
10431     TEST_DrawLevelField(x, y);
10432
10433     if (GFX_CRUMBLED(new_element))
10434       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10435   }
10436
10437   // check if element under the player changes from accessible to unaccessible
10438   // (needed for special case of dropping element which then changes)
10439   // (must be checked after creating new element for walkable group elements)
10440   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10441       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10442   {
10443     Bang(x, y);
10444
10445     return;
10446   }
10447
10448   // "ChangeCount" not set yet to allow "entered by player" change one time
10449   if (new_element_is_player)
10450     RelocatePlayer(x, y, new_element);
10451
10452   if (is_change)
10453     ChangeCount[x][y]++;        // count number of changes in the same frame
10454
10455   TestIfBadThingTouchesPlayer(x, y);
10456   TestIfPlayerTouchesCustomElement(x, y);
10457   TestIfElementTouchesCustomElement(x, y);
10458 }
10459
10460 static void CreateField(int x, int y, int element)
10461 {
10462   CreateFieldExt(x, y, element, FALSE);
10463 }
10464
10465 static void CreateElementFromChange(int x, int y, int element)
10466 {
10467   element = GET_VALID_RUNTIME_ELEMENT(element);
10468
10469   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10470   {
10471     int old_element = Tile[x][y];
10472
10473     // prevent changed element from moving in same engine frame
10474     // unless both old and new element can either fall or move
10475     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10476         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10477       Stop[x][y] = TRUE;
10478   }
10479
10480   CreateFieldExt(x, y, element, TRUE);
10481 }
10482
10483 static boolean ChangeElement(int x, int y, int element, int page)
10484 {
10485   struct ElementInfo *ei = &element_info[element];
10486   struct ElementChangeInfo *change = &ei->change_page[page];
10487   int ce_value = CustomValue[x][y];
10488   int ce_score = ei->collect_score;
10489   int target_element;
10490   int old_element = Tile[x][y];
10491
10492   // always use default change event to prevent running into a loop
10493   if (ChangeEvent[x][y] == -1)
10494     ChangeEvent[x][y] = CE_DELAY;
10495
10496   if (ChangeEvent[x][y] == CE_DELAY)
10497   {
10498     // reset actual trigger element, trigger player and action element
10499     change->actual_trigger_element = EL_EMPTY;
10500     change->actual_trigger_player = EL_EMPTY;
10501     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10502     change->actual_trigger_side = CH_SIDE_NONE;
10503     change->actual_trigger_ce_value = 0;
10504     change->actual_trigger_ce_score = 0;
10505   }
10506
10507   // do not change elements more than a specified maximum number of changes
10508   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10509     return FALSE;
10510
10511   ChangeCount[x][y]++;          // count number of changes in the same frame
10512
10513   if (change->explode)
10514   {
10515     Bang(x, y);
10516
10517     return TRUE;
10518   }
10519
10520   if (change->use_target_content)
10521   {
10522     boolean complete_replace = TRUE;
10523     boolean can_replace[3][3];
10524     int xx, yy;
10525
10526     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10527     {
10528       boolean is_empty;
10529       boolean is_walkable;
10530       boolean is_diggable;
10531       boolean is_collectible;
10532       boolean is_removable;
10533       boolean is_destructible;
10534       int ex = x + xx - 1;
10535       int ey = y + yy - 1;
10536       int content_element = change->target_content.e[xx][yy];
10537       int e;
10538
10539       can_replace[xx][yy] = TRUE;
10540
10541       if (ex == x && ey == y)   // do not check changing element itself
10542         continue;
10543
10544       if (content_element == EL_EMPTY_SPACE)
10545       {
10546         can_replace[xx][yy] = FALSE;    // do not replace border with space
10547
10548         continue;
10549       }
10550
10551       if (!IN_LEV_FIELD(ex, ey))
10552       {
10553         can_replace[xx][yy] = FALSE;
10554         complete_replace = FALSE;
10555
10556         continue;
10557       }
10558
10559       e = Tile[ex][ey];
10560
10561       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10562         e = MovingOrBlocked2Element(ex, ey);
10563
10564       is_empty = (IS_FREE(ex, ey) ||
10565                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10566
10567       is_walkable     = (is_empty || IS_WALKABLE(e));
10568       is_diggable     = (is_empty || IS_DIGGABLE(e));
10569       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10570       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10571       is_removable    = (is_diggable || is_collectible);
10572
10573       can_replace[xx][yy] =
10574         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10575           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10576           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10577           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10578           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10579           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10580          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10581
10582       if (!can_replace[xx][yy])
10583         complete_replace = FALSE;
10584     }
10585
10586     if (!change->only_if_complete || complete_replace)
10587     {
10588       boolean something_has_changed = FALSE;
10589
10590       if (change->only_if_complete && change->use_random_replace &&
10591           RND(100) < change->random_percentage)
10592         return FALSE;
10593
10594       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10595       {
10596         int ex = x + xx - 1;
10597         int ey = y + yy - 1;
10598         int content_element;
10599
10600         if (can_replace[xx][yy] && (!change->use_random_replace ||
10601                                     RND(100) < change->random_percentage))
10602         {
10603           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10604             RemoveMovingField(ex, ey);
10605
10606           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10607
10608           content_element = change->target_content.e[xx][yy];
10609           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10610                                               ce_value, ce_score);
10611
10612           CreateElementFromChange(ex, ey, target_element);
10613
10614           something_has_changed = TRUE;
10615
10616           // for symmetry reasons, freeze newly created border elements
10617           if (ex != x || ey != y)
10618             Stop[ex][ey] = TRUE;        // no more moving in this frame
10619         }
10620       }
10621
10622       if (something_has_changed)
10623       {
10624         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10625         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10626       }
10627     }
10628   }
10629   else
10630   {
10631     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10632                                         ce_value, ce_score);
10633
10634     if (element == EL_DIAGONAL_GROWING ||
10635         element == EL_DIAGONAL_SHRINKING)
10636     {
10637       target_element = Store[x][y];
10638
10639       Store[x][y] = EL_EMPTY;
10640     }
10641
10642     CreateElementFromChange(x, y, target_element);
10643
10644     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10645     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10646   }
10647
10648   // this uses direct change before indirect change
10649   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10650
10651   return TRUE;
10652 }
10653
10654 static void HandleElementChange(int x, int y, int page)
10655 {
10656   int element = MovingOrBlocked2Element(x, y);
10657   struct ElementInfo *ei = &element_info[element];
10658   struct ElementChangeInfo *change = &ei->change_page[page];
10659   boolean handle_action_before_change = FALSE;
10660
10661 #ifdef DEBUG
10662   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10663       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10664   {
10665     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10666           x, y, element, element_info[element].token_name);
10667     Debug("game:playing:HandleElementChange", "This should never happen!");
10668   }
10669 #endif
10670
10671   // this can happen with classic bombs on walkable, changing elements
10672   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10673   {
10674     return;
10675   }
10676
10677   if (ChangeDelay[x][y] == 0)           // initialize element change
10678   {
10679     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10680
10681     if (change->can_change)
10682     {
10683       // !!! not clear why graphic animation should be reset at all here !!!
10684       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10685       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10686
10687       /*
10688         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10689
10690         When using an animation frame delay of 1 (this only happens with
10691         "sp_zonk.moving.left/right" in the classic graphics), the default
10692         (non-moving) animation shows wrong animation frames (while the
10693         moving animation, like "sp_zonk.moving.left/right", is correct,
10694         so this graphical bug never shows up with the classic graphics).
10695         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10696         be drawn instead of the correct frames 0,1,2,3. This is caused by
10697         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10698         an element change: First when the change delay ("ChangeDelay[][]")
10699         counter has reached zero after decrementing, then a second time in
10700         the next frame (after "GfxFrame[][]" was already incremented) when
10701         "ChangeDelay[][]" is reset to the initial delay value again.
10702
10703         This causes frame 0 to be drawn twice, while the last frame won't
10704         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10705
10706         As some animations may already be cleverly designed around this bug
10707         (at least the "Snake Bite" snake tail animation does this), it cannot
10708         simply be fixed here without breaking such existing animations.
10709         Unfortunately, it cannot easily be detected if a graphics set was
10710         designed "before" or "after" the bug was fixed. As a workaround,
10711         a new graphics set option "game.graphics_engine_version" was added
10712         to be able to specify the game's major release version for which the
10713         graphics set was designed, which can then be used to decide if the
10714         bugfix should be used (version 4 and above) or not (version 3 or
10715         below, or if no version was specified at all, as with old sets).
10716
10717         (The wrong/fixed animation frames can be tested with the test level set
10718         "test_gfxframe" and level "000", which contains a specially prepared
10719         custom element at level position (x/y) == (11/9) which uses the zonk
10720         animation mentioned above. Using "game.graphics_engine_version: 4"
10721         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10722         This can also be seen from the debug output for this test element.)
10723       */
10724
10725       // when a custom element is about to change (for example by change delay),
10726       // do not reset graphic animation when the custom element is moving
10727       if (game.graphics_engine_version < 4 &&
10728           !IS_MOVING(x, y))
10729       {
10730         ResetGfxAnimation(x, y);
10731         ResetRandomAnimationValue(x, y);
10732       }
10733
10734       if (change->pre_change_function)
10735         change->pre_change_function(x, y);
10736     }
10737   }
10738
10739   ChangeDelay[x][y]--;
10740
10741   if (ChangeDelay[x][y] != 0)           // continue element change
10742   {
10743     if (change->can_change)
10744     {
10745       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10746
10747       if (IS_ANIMATED(graphic))
10748         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10749
10750       if (change->change_function)
10751         change->change_function(x, y);
10752     }
10753   }
10754   else                                  // finish element change
10755   {
10756     if (ChangePage[x][y] != -1)         // remember page from delayed change
10757     {
10758       page = ChangePage[x][y];
10759       ChangePage[x][y] = -1;
10760
10761       change = &ei->change_page[page];
10762     }
10763
10764     if (IS_MOVING(x, y))                // never change a running system ;-)
10765     {
10766       ChangeDelay[x][y] = 1;            // try change after next move step
10767       ChangePage[x][y] = page;          // remember page to use for change
10768
10769       return;
10770     }
10771
10772     // special case: set new level random seed before changing element
10773     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10774       handle_action_before_change = TRUE;
10775
10776     if (change->has_action && handle_action_before_change)
10777       ExecuteCustomElementAction(x, y, element, page);
10778
10779     if (change->can_change)
10780     {
10781       if (ChangeElement(x, y, element, page))
10782       {
10783         if (change->post_change_function)
10784           change->post_change_function(x, y);
10785       }
10786     }
10787
10788     if (change->has_action && !handle_action_before_change)
10789       ExecuteCustomElementAction(x, y, element, page);
10790   }
10791 }
10792
10793 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10794                                               int trigger_element,
10795                                               int trigger_event,
10796                                               int trigger_player,
10797                                               int trigger_side,
10798                                               int trigger_page)
10799 {
10800   boolean change_done_any = FALSE;
10801   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10802   int i;
10803
10804   if (!(trigger_events[trigger_element][trigger_event]))
10805     return FALSE;
10806
10807   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10808
10809   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10810   {
10811     int element = EL_CUSTOM_START + i;
10812     boolean change_done = FALSE;
10813     int p;
10814
10815     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10816         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10817       continue;
10818
10819     for (p = 0; p < element_info[element].num_change_pages; p++)
10820     {
10821       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10822
10823       if (change->can_change_or_has_action &&
10824           change->has_event[trigger_event] &&
10825           change->trigger_side & trigger_side &&
10826           change->trigger_player & trigger_player &&
10827           change->trigger_page & trigger_page_bits &&
10828           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10829       {
10830         change->actual_trigger_element = trigger_element;
10831         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10832         change->actual_trigger_player_bits = trigger_player;
10833         change->actual_trigger_side = trigger_side;
10834         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10835         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10836
10837         if ((change->can_change && !change_done) || change->has_action)
10838         {
10839           int x, y;
10840
10841           SCAN_PLAYFIELD(x, y)
10842           {
10843             if (Tile[x][y] == element)
10844             {
10845               if (change->can_change && !change_done)
10846               {
10847                 // if element already changed in this frame, not only prevent
10848                 // another element change (checked in ChangeElement()), but
10849                 // also prevent additional element actions for this element
10850
10851                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10852                     !level.use_action_after_change_bug)
10853                   continue;
10854
10855                 ChangeDelay[x][y] = 1;
10856                 ChangeEvent[x][y] = trigger_event;
10857
10858                 HandleElementChange(x, y, p);
10859               }
10860               else if (change->has_action)
10861               {
10862                 // if element already changed in this frame, not only prevent
10863                 // another element change (checked in ChangeElement()), but
10864                 // also prevent additional element actions for this element
10865
10866                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10867                     !level.use_action_after_change_bug)
10868                   continue;
10869
10870                 ExecuteCustomElementAction(x, y, element, p);
10871                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10872               }
10873             }
10874           }
10875
10876           if (change->can_change)
10877           {
10878             change_done = TRUE;
10879             change_done_any = TRUE;
10880           }
10881         }
10882       }
10883     }
10884   }
10885
10886   RECURSION_LOOP_DETECTION_END();
10887
10888   return change_done_any;
10889 }
10890
10891 static boolean CheckElementChangeExt(int x, int y,
10892                                      int element,
10893                                      int trigger_element,
10894                                      int trigger_event,
10895                                      int trigger_player,
10896                                      int trigger_side)
10897 {
10898   boolean change_done = FALSE;
10899   int p;
10900
10901   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10902       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10903     return FALSE;
10904
10905   if (Tile[x][y] == EL_BLOCKED)
10906   {
10907     Blocked2Moving(x, y, &x, &y);
10908     element = Tile[x][y];
10909   }
10910
10911   // check if element has already changed or is about to change after moving
10912   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10913        Tile[x][y] != element) ||
10914
10915       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10916        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10917         ChangePage[x][y] != -1)))
10918     return FALSE;
10919
10920   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10921
10922   for (p = 0; p < element_info[element].num_change_pages; p++)
10923   {
10924     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10925
10926     /* check trigger element for all events where the element that is checked
10927        for changing interacts with a directly adjacent element -- this is
10928        different to element changes that affect other elements to change on the
10929        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10930     boolean check_trigger_element =
10931       (trigger_event == CE_TOUCHING_X ||
10932        trigger_event == CE_HITTING_X ||
10933        trigger_event == CE_HIT_BY_X ||
10934        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
10935
10936     if (change->can_change_or_has_action &&
10937         change->has_event[trigger_event] &&
10938         change->trigger_side & trigger_side &&
10939         change->trigger_player & trigger_player &&
10940         (!check_trigger_element ||
10941          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10942     {
10943       change->actual_trigger_element = trigger_element;
10944       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10945       change->actual_trigger_player_bits = trigger_player;
10946       change->actual_trigger_side = trigger_side;
10947       change->actual_trigger_ce_value = CustomValue[x][y];
10948       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10949
10950       // special case: trigger element not at (x,y) position for some events
10951       if (check_trigger_element)
10952       {
10953         static struct
10954         {
10955           int dx, dy;
10956         } move_xy[] =
10957           {
10958             {  0,  0 },
10959             { -1,  0 },
10960             { +1,  0 },
10961             {  0,  0 },
10962             {  0, -1 },
10963             {  0,  0 }, { 0, 0 }, { 0, 0 },
10964             {  0, +1 }
10965           };
10966
10967         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10968         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10969
10970         change->actual_trigger_ce_value = CustomValue[xx][yy];
10971         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10972       }
10973
10974       if (change->can_change && !change_done)
10975       {
10976         ChangeDelay[x][y] = 1;
10977         ChangeEvent[x][y] = trigger_event;
10978
10979         HandleElementChange(x, y, p);
10980
10981         change_done = TRUE;
10982       }
10983       else if (change->has_action)
10984       {
10985         ExecuteCustomElementAction(x, y, element, p);
10986         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10987       }
10988     }
10989   }
10990
10991   RECURSION_LOOP_DETECTION_END();
10992
10993   return change_done;
10994 }
10995
10996 static void PlayPlayerSound(struct PlayerInfo *player)
10997 {
10998   int jx = player->jx, jy = player->jy;
10999   int sound_element = player->artwork_element;
11000   int last_action = player->last_action_waiting;
11001   int action = player->action_waiting;
11002
11003   if (player->is_waiting)
11004   {
11005     if (action != last_action)
11006       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11007     else
11008       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11009   }
11010   else
11011   {
11012     if (action != last_action)
11013       StopSound(element_info[sound_element].sound[last_action]);
11014
11015     if (last_action == ACTION_SLEEPING)
11016       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11017   }
11018 }
11019
11020 static void PlayAllPlayersSound(void)
11021 {
11022   int i;
11023
11024   for (i = 0; i < MAX_PLAYERS; i++)
11025     if (stored_player[i].active)
11026       PlayPlayerSound(&stored_player[i]);
11027 }
11028
11029 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11030 {
11031   boolean last_waiting = player->is_waiting;
11032   int move_dir = player->MovDir;
11033
11034   player->dir_waiting = move_dir;
11035   player->last_action_waiting = player->action_waiting;
11036
11037   if (is_waiting)
11038   {
11039     if (!last_waiting)          // not waiting -> waiting
11040     {
11041       player->is_waiting = TRUE;
11042
11043       player->frame_counter_bored =
11044         FrameCounter +
11045         game.player_boring_delay_fixed +
11046         GetSimpleRandom(game.player_boring_delay_random);
11047       player->frame_counter_sleeping =
11048         FrameCounter +
11049         game.player_sleeping_delay_fixed +
11050         GetSimpleRandom(game.player_sleeping_delay_random);
11051
11052       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11053     }
11054
11055     if (game.player_sleeping_delay_fixed +
11056         game.player_sleeping_delay_random > 0 &&
11057         player->anim_delay_counter == 0 &&
11058         player->post_delay_counter == 0 &&
11059         FrameCounter >= player->frame_counter_sleeping)
11060       player->is_sleeping = TRUE;
11061     else if (game.player_boring_delay_fixed +
11062              game.player_boring_delay_random > 0 &&
11063              FrameCounter >= player->frame_counter_bored)
11064       player->is_bored = TRUE;
11065
11066     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11067                               player->is_bored ? ACTION_BORING :
11068                               ACTION_WAITING);
11069
11070     if (player->is_sleeping && player->use_murphy)
11071     {
11072       // special case for sleeping Murphy when leaning against non-free tile
11073
11074       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11075           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11076            !IS_MOVING(player->jx - 1, player->jy)))
11077         move_dir = MV_LEFT;
11078       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11079                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11080                 !IS_MOVING(player->jx + 1, player->jy)))
11081         move_dir = MV_RIGHT;
11082       else
11083         player->is_sleeping = FALSE;
11084
11085       player->dir_waiting = move_dir;
11086     }
11087
11088     if (player->is_sleeping)
11089     {
11090       if (player->num_special_action_sleeping > 0)
11091       {
11092         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11093         {
11094           int last_special_action = player->special_action_sleeping;
11095           int num_special_action = player->num_special_action_sleeping;
11096           int special_action =
11097             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11098              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11099              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11100              last_special_action + 1 : ACTION_SLEEPING);
11101           int special_graphic =
11102             el_act_dir2img(player->artwork_element, special_action, move_dir);
11103
11104           player->anim_delay_counter =
11105             graphic_info[special_graphic].anim_delay_fixed +
11106             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11107           player->post_delay_counter =
11108             graphic_info[special_graphic].post_delay_fixed +
11109             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11110
11111           player->special_action_sleeping = special_action;
11112         }
11113
11114         if (player->anim_delay_counter > 0)
11115         {
11116           player->action_waiting = player->special_action_sleeping;
11117           player->anim_delay_counter--;
11118         }
11119         else if (player->post_delay_counter > 0)
11120         {
11121           player->post_delay_counter--;
11122         }
11123       }
11124     }
11125     else if (player->is_bored)
11126     {
11127       if (player->num_special_action_bored > 0)
11128       {
11129         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11130         {
11131           int special_action =
11132             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11133           int special_graphic =
11134             el_act_dir2img(player->artwork_element, special_action, move_dir);
11135
11136           player->anim_delay_counter =
11137             graphic_info[special_graphic].anim_delay_fixed +
11138             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11139           player->post_delay_counter =
11140             graphic_info[special_graphic].post_delay_fixed +
11141             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11142
11143           player->special_action_bored = special_action;
11144         }
11145
11146         if (player->anim_delay_counter > 0)
11147         {
11148           player->action_waiting = player->special_action_bored;
11149           player->anim_delay_counter--;
11150         }
11151         else if (player->post_delay_counter > 0)
11152         {
11153           player->post_delay_counter--;
11154         }
11155       }
11156     }
11157   }
11158   else if (last_waiting)        // waiting -> not waiting
11159   {
11160     player->is_waiting = FALSE;
11161     player->is_bored = FALSE;
11162     player->is_sleeping = FALSE;
11163
11164     player->frame_counter_bored = -1;
11165     player->frame_counter_sleeping = -1;
11166
11167     player->anim_delay_counter = 0;
11168     player->post_delay_counter = 0;
11169
11170     player->dir_waiting = player->MovDir;
11171     player->action_waiting = ACTION_DEFAULT;
11172
11173     player->special_action_bored = ACTION_DEFAULT;
11174     player->special_action_sleeping = ACTION_DEFAULT;
11175   }
11176 }
11177
11178 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11179 {
11180   if ((!player->is_moving  && player->was_moving) ||
11181       (player->MovPos == 0 && player->was_moving) ||
11182       (player->is_snapping && !player->was_snapping) ||
11183       (player->is_dropping && !player->was_dropping))
11184   {
11185     if (!CheckSaveEngineSnapshotToList())
11186       return;
11187
11188     player->was_moving = FALSE;
11189     player->was_snapping = TRUE;
11190     player->was_dropping = TRUE;
11191   }
11192   else
11193   {
11194     if (player->is_moving)
11195       player->was_moving = TRUE;
11196
11197     if (!player->is_snapping)
11198       player->was_snapping = FALSE;
11199
11200     if (!player->is_dropping)
11201       player->was_dropping = FALSE;
11202   }
11203 }
11204
11205 static void CheckSingleStepMode(struct PlayerInfo *player)
11206 {
11207   if (tape.single_step && tape.recording && !tape.pausing)
11208   {
11209     /* as it is called "single step mode", just return to pause mode when the
11210        player stopped moving after one tile (or never starts moving at all) */
11211     if (!player->is_moving &&
11212         !player->is_pushing &&
11213         !player->is_dropping_pressed)
11214       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11215   }
11216
11217   CheckSaveEngineSnapshot(player);
11218 }
11219
11220 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11221 {
11222   int left      = player_action & JOY_LEFT;
11223   int right     = player_action & JOY_RIGHT;
11224   int up        = player_action & JOY_UP;
11225   int down      = player_action & JOY_DOWN;
11226   int button1   = player_action & JOY_BUTTON_1;
11227   int button2   = player_action & JOY_BUTTON_2;
11228   int dx        = (left ? -1 : right ? 1 : 0);
11229   int dy        = (up   ? -1 : down  ? 1 : 0);
11230
11231   if (!player->active || tape.pausing)
11232     return 0;
11233
11234   if (player_action)
11235   {
11236     if (button1)
11237       SnapField(player, dx, dy);
11238     else
11239     {
11240       if (button2)
11241         DropElement(player);
11242
11243       MovePlayer(player, dx, dy);
11244     }
11245
11246     CheckSingleStepMode(player);
11247
11248     SetPlayerWaiting(player, FALSE);
11249
11250     return player_action;
11251   }
11252   else
11253   {
11254     // no actions for this player (no input at player's configured device)
11255
11256     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11257     SnapField(player, 0, 0);
11258     CheckGravityMovementWhenNotMoving(player);
11259
11260     if (player->MovPos == 0)
11261       SetPlayerWaiting(player, TRUE);
11262
11263     if (player->MovPos == 0)    // needed for tape.playing
11264       player->is_moving = FALSE;
11265
11266     player->is_dropping = FALSE;
11267     player->is_dropping_pressed = FALSE;
11268     player->drop_pressed_delay = 0;
11269
11270     CheckSingleStepMode(player);
11271
11272     return 0;
11273   }
11274 }
11275
11276 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11277                                          byte *tape_action)
11278 {
11279   if (!tape.use_mouse_actions)
11280     return;
11281
11282   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11283   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11284   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11285 }
11286
11287 static void SetTapeActionFromMouseAction(byte *tape_action,
11288                                          struct MouseActionInfo *mouse_action)
11289 {
11290   if (!tape.use_mouse_actions)
11291     return;
11292
11293   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11294   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11295   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11296 }
11297
11298 static void CheckLevelSolved(void)
11299 {
11300   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11301   {
11302     if (game_em.level_solved &&
11303         !game_em.game_over)                             // game won
11304     {
11305       LevelSolved();
11306
11307       game_em.game_over = TRUE;
11308
11309       game.all_players_gone = TRUE;
11310     }
11311
11312     if (game_em.game_over)                              // game lost
11313       game.all_players_gone = TRUE;
11314   }
11315   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11316   {
11317     if (game_sp.level_solved &&
11318         !game_sp.game_over)                             // game won
11319     {
11320       LevelSolved();
11321
11322       game_sp.game_over = TRUE;
11323
11324       game.all_players_gone = TRUE;
11325     }
11326
11327     if (game_sp.game_over)                              // game lost
11328       game.all_players_gone = TRUE;
11329   }
11330   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11331   {
11332     if (game_mm.level_solved &&
11333         !game_mm.game_over)                             // game won
11334     {
11335       LevelSolved();
11336
11337       game_mm.game_over = TRUE;
11338
11339       game.all_players_gone = TRUE;
11340     }
11341
11342     if (game_mm.game_over)                              // game lost
11343       game.all_players_gone = TRUE;
11344   }
11345 }
11346
11347 static void CheckLevelTime(void)
11348 {
11349   int i;
11350
11351   if (TimeFrames >= FRAMES_PER_SECOND)
11352   {
11353     TimeFrames = 0;
11354     TapeTime++;
11355
11356     for (i = 0; i < MAX_PLAYERS; i++)
11357     {
11358       struct PlayerInfo *player = &stored_player[i];
11359
11360       if (SHIELD_ON(player))
11361       {
11362         player->shield_normal_time_left--;
11363
11364         if (player->shield_deadly_time_left > 0)
11365           player->shield_deadly_time_left--;
11366       }
11367     }
11368
11369     if (!game.LevelSolved && !level.use_step_counter)
11370     {
11371       TimePlayed++;
11372
11373       if (TimeLeft > 0)
11374       {
11375         TimeLeft--;
11376
11377         if (TimeLeft <= 10 && setup.time_limit)
11378           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11379
11380         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11381            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11382
11383         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11384
11385         if (!TimeLeft && setup.time_limit)
11386         {
11387           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11388             game_em.lev->killed_out_of_time = TRUE;
11389           else
11390             for (i = 0; i < MAX_PLAYERS; i++)
11391               KillPlayer(&stored_player[i]);
11392         }
11393       }
11394       else if (game.no_time_limit && !game.all_players_gone)
11395       {
11396         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11397       }
11398
11399       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11400     }
11401
11402     if (tape.recording || tape.playing)
11403       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11404   }
11405
11406   if (tape.recording || tape.playing)
11407     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11408
11409   UpdateAndDisplayGameControlValues();
11410 }
11411
11412 void AdvanceFrameAndPlayerCounters(int player_nr)
11413 {
11414   int i;
11415
11416   // advance frame counters (global frame counter and time frame counter)
11417   FrameCounter++;
11418   TimeFrames++;
11419
11420   // advance player counters (counters for move delay, move animation etc.)
11421   for (i = 0; i < MAX_PLAYERS; i++)
11422   {
11423     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11424     int move_delay_value = stored_player[i].move_delay_value;
11425     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11426
11427     if (!advance_player_counters)       // not all players may be affected
11428       continue;
11429
11430     if (move_frames == 0)       // less than one move per game frame
11431     {
11432       int stepsize = TILEX / move_delay_value;
11433       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11434       int count = (stored_player[i].is_moving ?
11435                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11436
11437       if (count % delay == 0)
11438         move_frames = 1;
11439     }
11440
11441     stored_player[i].Frame += move_frames;
11442
11443     if (stored_player[i].MovPos != 0)
11444       stored_player[i].StepFrame += move_frames;
11445
11446     if (stored_player[i].move_delay > 0)
11447       stored_player[i].move_delay--;
11448
11449     // due to bugs in previous versions, counter must count up, not down
11450     if (stored_player[i].push_delay != -1)
11451       stored_player[i].push_delay++;
11452
11453     if (stored_player[i].drop_delay > 0)
11454       stored_player[i].drop_delay--;
11455
11456     if (stored_player[i].is_dropping_pressed)
11457       stored_player[i].drop_pressed_delay++;
11458   }
11459 }
11460
11461 void StartGameActions(boolean init_network_game, boolean record_tape,
11462                       int random_seed)
11463 {
11464   unsigned int new_random_seed = InitRND(random_seed);
11465
11466   if (record_tape)
11467     TapeStartRecording(new_random_seed);
11468
11469   if (init_network_game)
11470   {
11471     SendToServer_LevelFile();
11472     SendToServer_StartPlaying();
11473
11474     return;
11475   }
11476
11477   InitGame();
11478 }
11479
11480 static void GameActionsExt(void)
11481 {
11482 #if 0
11483   static unsigned int game_frame_delay = 0;
11484 #endif
11485   unsigned int game_frame_delay_value;
11486   byte *recorded_player_action;
11487   byte summarized_player_action = 0;
11488   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11489   int i;
11490
11491   // detect endless loops, caused by custom element programming
11492   if (recursion_loop_detected && recursion_loop_depth == 0)
11493   {
11494     char *message = getStringCat3("Internal Error! Element ",
11495                                   EL_NAME(recursion_loop_element),
11496                                   " caused endless loop! Quit the game?");
11497
11498     Warn("element '%s' caused endless loop in game engine",
11499          EL_NAME(recursion_loop_element));
11500
11501     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11502
11503     recursion_loop_detected = FALSE;    // if game should be continued
11504
11505     free(message);
11506
11507     return;
11508   }
11509
11510   if (game.restart_level)
11511     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11512
11513   CheckLevelSolved();
11514
11515   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11516     GameWon();
11517
11518   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11519     TapeStop();
11520
11521   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11522     return;
11523
11524   game_frame_delay_value =
11525     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11526
11527   if (tape.playing && tape.warp_forward && !tape.pausing)
11528     game_frame_delay_value = 0;
11529
11530   SetVideoFrameDelay(game_frame_delay_value);
11531
11532   // (de)activate virtual buttons depending on current game status
11533   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11534   {
11535     if (game.all_players_gone)  // if no players there to be controlled anymore
11536       SetOverlayActive(FALSE);
11537     else if (!tape.playing)     // if game continues after tape stopped playing
11538       SetOverlayActive(TRUE);
11539   }
11540
11541 #if 0
11542 #if 0
11543   // ---------- main game synchronization point ----------
11544
11545   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11546
11547   Debug("game:playing:skip", "skip == %d", skip);
11548
11549 #else
11550   // ---------- main game synchronization point ----------
11551
11552   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11553 #endif
11554 #endif
11555
11556   if (network_playing && !network_player_action_received)
11557   {
11558     // try to get network player actions in time
11559
11560     // last chance to get network player actions without main loop delay
11561     HandleNetworking();
11562
11563     // game was quit by network peer
11564     if (game_status != GAME_MODE_PLAYING)
11565       return;
11566
11567     // check if network player actions still missing and game still running
11568     if (!network_player_action_received && !checkGameEnded())
11569       return;           // failed to get network player actions in time
11570
11571     // do not yet reset "network_player_action_received" (for tape.pausing)
11572   }
11573
11574   if (tape.pausing)
11575     return;
11576
11577   // at this point we know that we really continue executing the game
11578
11579   network_player_action_received = FALSE;
11580
11581   // when playing tape, read previously recorded player input from tape data
11582   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11583
11584   local_player->effective_mouse_action = local_player->mouse_action;
11585
11586   if (recorded_player_action != NULL)
11587     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11588                                  recorded_player_action);
11589
11590   // TapePlayAction() may return NULL when toggling to "pause before death"
11591   if (tape.pausing)
11592     return;
11593
11594   if (tape.set_centered_player)
11595   {
11596     game.centered_player_nr_next = tape.centered_player_nr_next;
11597     game.set_centered_player = TRUE;
11598   }
11599
11600   for (i = 0; i < MAX_PLAYERS; i++)
11601   {
11602     summarized_player_action |= stored_player[i].action;
11603
11604     if (!network_playing && (game.team_mode || tape.playing))
11605       stored_player[i].effective_action = stored_player[i].action;
11606   }
11607
11608   if (network_playing && !checkGameEnded())
11609     SendToServer_MovePlayer(summarized_player_action);
11610
11611   // summarize all actions at local players mapped input device position
11612   // (this allows using different input devices in single player mode)
11613   if (!network.enabled && !game.team_mode)
11614     stored_player[map_player_action[local_player->index_nr]].effective_action =
11615       summarized_player_action;
11616
11617   // summarize all actions at centered player in local team mode
11618   if (tape.recording &&
11619       setup.team_mode && !network.enabled &&
11620       setup.input_on_focus &&
11621       game.centered_player_nr != -1)
11622   {
11623     for (i = 0; i < MAX_PLAYERS; i++)
11624       stored_player[map_player_action[i]].effective_action =
11625         (i == game.centered_player_nr ? summarized_player_action : 0);
11626   }
11627
11628   if (recorded_player_action != NULL)
11629     for (i = 0; i < MAX_PLAYERS; i++)
11630       stored_player[i].effective_action = recorded_player_action[i];
11631
11632   for (i = 0; i < MAX_PLAYERS; i++)
11633   {
11634     tape_action[i] = stored_player[i].effective_action;
11635
11636     /* (this may happen in the RND game engine if a player was not present on
11637        the playfield on level start, but appeared later from a custom element */
11638     if (setup.team_mode &&
11639         tape.recording &&
11640         tape_action[i] &&
11641         !tape.player_participates[i])
11642       tape.player_participates[i] = TRUE;
11643   }
11644
11645   SetTapeActionFromMouseAction(tape_action,
11646                                &local_player->effective_mouse_action);
11647
11648   // only record actions from input devices, but not programmed actions
11649   if (tape.recording)
11650     TapeRecordAction(tape_action);
11651
11652   // remember if game was played (especially after tape stopped playing)
11653   if (!tape.playing && summarized_player_action)
11654     game.GamePlayed = TRUE;
11655
11656 #if USE_NEW_PLAYER_ASSIGNMENTS
11657   // !!! also map player actions in single player mode !!!
11658   // if (game.team_mode)
11659   if (1)
11660   {
11661     byte mapped_action[MAX_PLAYERS];
11662
11663 #if DEBUG_PLAYER_ACTIONS
11664     for (i = 0; i < MAX_PLAYERS; i++)
11665       DebugContinued("", "%d, ", stored_player[i].effective_action);
11666 #endif
11667
11668     for (i = 0; i < MAX_PLAYERS; i++)
11669       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11670
11671     for (i = 0; i < MAX_PLAYERS; i++)
11672       stored_player[i].effective_action = mapped_action[i];
11673
11674 #if DEBUG_PLAYER_ACTIONS
11675     DebugContinued("", "=> ");
11676     for (i = 0; i < MAX_PLAYERS; i++)
11677       DebugContinued("", "%d, ", stored_player[i].effective_action);
11678     DebugContinued("game:playing:player", "\n");
11679 #endif
11680   }
11681 #if DEBUG_PLAYER_ACTIONS
11682   else
11683   {
11684     for (i = 0; i < MAX_PLAYERS; i++)
11685       DebugContinued("", "%d, ", stored_player[i].effective_action);
11686     DebugContinued("game:playing:player", "\n");
11687   }
11688 #endif
11689 #endif
11690
11691   for (i = 0; i < MAX_PLAYERS; i++)
11692   {
11693     // allow engine snapshot in case of changed movement attempt
11694     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11695         (stored_player[i].effective_action & KEY_MOTION))
11696       game.snapshot.changed_action = TRUE;
11697
11698     // allow engine snapshot in case of snapping/dropping attempt
11699     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11700         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11701       game.snapshot.changed_action = TRUE;
11702
11703     game.snapshot.last_action[i] = stored_player[i].effective_action;
11704   }
11705
11706   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11707   {
11708     GameActions_EM_Main();
11709   }
11710   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11711   {
11712     GameActions_SP_Main();
11713   }
11714   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11715   {
11716     GameActions_MM_Main();
11717   }
11718   else
11719   {
11720     GameActions_RND_Main();
11721   }
11722
11723   BlitScreenToBitmap(backbuffer);
11724
11725   CheckLevelSolved();
11726   CheckLevelTime();
11727
11728   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11729
11730   if (global.show_frames_per_second)
11731   {
11732     static unsigned int fps_counter = 0;
11733     static int fps_frames = 0;
11734     unsigned int fps_delay_ms = Counter() - fps_counter;
11735
11736     fps_frames++;
11737
11738     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11739     {
11740       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11741
11742       fps_frames = 0;
11743       fps_counter = Counter();
11744
11745       // always draw FPS to screen after FPS value was updated
11746       redraw_mask |= REDRAW_FPS;
11747     }
11748
11749     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11750     if (GetDrawDeactivationMask() == REDRAW_NONE)
11751       redraw_mask |= REDRAW_FPS;
11752   }
11753 }
11754
11755 static void GameActions_CheckSaveEngineSnapshot(void)
11756 {
11757   if (!game.snapshot.save_snapshot)
11758     return;
11759
11760   // clear flag for saving snapshot _before_ saving snapshot
11761   game.snapshot.save_snapshot = FALSE;
11762
11763   SaveEngineSnapshotToList();
11764 }
11765
11766 void GameActions(void)
11767 {
11768   GameActionsExt();
11769
11770   GameActions_CheckSaveEngineSnapshot();
11771 }
11772
11773 void GameActions_EM_Main(void)
11774 {
11775   byte effective_action[MAX_PLAYERS];
11776   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11777   int i;
11778
11779   for (i = 0; i < MAX_PLAYERS; i++)
11780     effective_action[i] = stored_player[i].effective_action;
11781
11782   GameActions_EM(effective_action, warp_mode);
11783 }
11784
11785 void GameActions_SP_Main(void)
11786 {
11787   byte effective_action[MAX_PLAYERS];
11788   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11789   int i;
11790
11791   for (i = 0; i < MAX_PLAYERS; i++)
11792     effective_action[i] = stored_player[i].effective_action;
11793
11794   GameActions_SP(effective_action, warp_mode);
11795
11796   for (i = 0; i < MAX_PLAYERS; i++)
11797   {
11798     if (stored_player[i].force_dropping)
11799       stored_player[i].action |= KEY_BUTTON_DROP;
11800
11801     stored_player[i].force_dropping = FALSE;
11802   }
11803 }
11804
11805 void GameActions_MM_Main(void)
11806 {
11807   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11808
11809   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11810 }
11811
11812 void GameActions_RND_Main(void)
11813 {
11814   GameActions_RND();
11815 }
11816
11817 void GameActions_RND(void)
11818 {
11819   static struct MouseActionInfo mouse_action_last = { 0 };
11820   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
11821   int magic_wall_x = 0, magic_wall_y = 0;
11822   int i, x, y, element, graphic, last_gfx_frame;
11823
11824   InitPlayfieldScanModeVars();
11825
11826   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11827   {
11828     SCAN_PLAYFIELD(x, y)
11829     {
11830       ChangeCount[x][y] = 0;
11831       ChangeEvent[x][y] = -1;
11832     }
11833   }
11834
11835   if (game.set_centered_player)
11836   {
11837     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11838
11839     // switching to "all players" only possible if all players fit to screen
11840     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11841     {
11842       game.centered_player_nr_next = game.centered_player_nr;
11843       game.set_centered_player = FALSE;
11844     }
11845
11846     // do not switch focus to non-existing (or non-active) player
11847     if (game.centered_player_nr_next >= 0 &&
11848         !stored_player[game.centered_player_nr_next].active)
11849     {
11850       game.centered_player_nr_next = game.centered_player_nr;
11851       game.set_centered_player = FALSE;
11852     }
11853   }
11854
11855   if (game.set_centered_player &&
11856       ScreenMovPos == 0)        // screen currently aligned at tile position
11857   {
11858     int sx, sy;
11859
11860     if (game.centered_player_nr_next == -1)
11861     {
11862       setScreenCenteredToAllPlayers(&sx, &sy);
11863     }
11864     else
11865     {
11866       sx = stored_player[game.centered_player_nr_next].jx;
11867       sy = stored_player[game.centered_player_nr_next].jy;
11868     }
11869
11870     game.centered_player_nr = game.centered_player_nr_next;
11871     game.set_centered_player = FALSE;
11872
11873     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11874     DrawGameDoorValues();
11875   }
11876
11877   for (i = 0; i < MAX_PLAYERS; i++)
11878   {
11879     int actual_player_action = stored_player[i].effective_action;
11880
11881 #if 1
11882     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11883        - rnd_equinox_tetrachloride 048
11884        - rnd_equinox_tetrachloride_ii 096
11885        - rnd_emanuel_schmieg 002
11886        - doctor_sloan_ww 001, 020
11887     */
11888     if (stored_player[i].MovPos == 0)
11889       CheckGravityMovement(&stored_player[i]);
11890 #endif
11891
11892     // overwrite programmed action with tape action
11893     if (stored_player[i].programmed_action)
11894       actual_player_action = stored_player[i].programmed_action;
11895
11896     PlayerActions(&stored_player[i], actual_player_action);
11897
11898     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11899   }
11900
11901   ScrollScreen(NULL, SCROLL_GO_ON);
11902
11903   /* for backwards compatibility, the following code emulates a fixed bug that
11904      occured when pushing elements (causing elements that just made their last
11905      pushing step to already (if possible) make their first falling step in the
11906      same game frame, which is bad); this code is also needed to use the famous
11907      "spring push bug" which is used in older levels and might be wanted to be
11908      used also in newer levels, but in this case the buggy pushing code is only
11909      affecting the "spring" element and no other elements */
11910
11911   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11912   {
11913     for (i = 0; i < MAX_PLAYERS; i++)
11914     {
11915       struct PlayerInfo *player = &stored_player[i];
11916       int x = player->jx;
11917       int y = player->jy;
11918
11919       if (player->active && player->is_pushing && player->is_moving &&
11920           IS_MOVING(x, y) &&
11921           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11922            Tile[x][y] == EL_SPRING))
11923       {
11924         ContinueMoving(x, y);
11925
11926         // continue moving after pushing (this is actually a bug)
11927         if (!IS_MOVING(x, y))
11928           Stop[x][y] = FALSE;
11929       }
11930     }
11931   }
11932
11933   SCAN_PLAYFIELD(x, y)
11934   {
11935     Last[x][y] = Tile[x][y];
11936
11937     ChangeCount[x][y] = 0;
11938     ChangeEvent[x][y] = -1;
11939
11940     // this must be handled before main playfield loop
11941     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
11942     {
11943       MovDelay[x][y]--;
11944       if (MovDelay[x][y] <= 0)
11945         RemoveField(x, y);
11946     }
11947
11948     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
11949     {
11950       MovDelay[x][y]--;
11951       if (MovDelay[x][y] <= 0)
11952       {
11953         RemoveField(x, y);
11954         TEST_DrawLevelField(x, y);
11955
11956         TestIfElementTouchesCustomElement(x, y);        // for empty space
11957       }
11958     }
11959
11960 #if DEBUG
11961     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11962     {
11963       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
11964             x, y);
11965       Debug("game:playing:GameActions_RND", "This should never happen!");
11966
11967       ChangePage[x][y] = -1;
11968     }
11969 #endif
11970
11971     Stop[x][y] = FALSE;
11972     if (WasJustMoving[x][y] > 0)
11973       WasJustMoving[x][y]--;
11974     if (WasJustFalling[x][y] > 0)
11975       WasJustFalling[x][y]--;
11976     if (CheckCollision[x][y] > 0)
11977       CheckCollision[x][y]--;
11978     if (CheckImpact[x][y] > 0)
11979       CheckImpact[x][y]--;
11980
11981     GfxFrame[x][y]++;
11982
11983     /* reset finished pushing action (not done in ContinueMoving() to allow
11984        continuous pushing animation for elements with zero push delay) */
11985     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11986     {
11987       ResetGfxAnimation(x, y);
11988       TEST_DrawLevelField(x, y);
11989     }
11990
11991 #if DEBUG
11992     if (IS_BLOCKED(x, y))
11993     {
11994       int oldx, oldy;
11995
11996       Blocked2Moving(x, y, &oldx, &oldy);
11997       if (!IS_MOVING(oldx, oldy))
11998       {
11999         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12000         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12001         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12002         Debug("game:playing:GameActions_RND", "This should never happen!");
12003       }
12004     }
12005 #endif
12006   }
12007
12008   if (mouse_action.button)
12009   {
12010     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12011
12012     x = mouse_action.lx;
12013     y = mouse_action.ly;
12014     element = Tile[x][y];
12015
12016     if (new_button)
12017     {
12018       CheckElementChange(x, y, element, EL_UNDEFINED, CE_CLICKED_BY_MOUSE);
12019       CheckTriggeredElementChange(x, y, element, CE_MOUSE_CLICKED_ON_X);
12020     }
12021
12022     CheckElementChange(x, y, element, EL_UNDEFINED, CE_PRESSED_BY_MOUSE);
12023     CheckTriggeredElementChange(x, y, element, CE_MOUSE_PRESSED_ON_X);
12024   }
12025
12026   SCAN_PLAYFIELD(x, y)
12027   {
12028     element = Tile[x][y];
12029     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12030     last_gfx_frame = GfxFrame[x][y];
12031
12032     ResetGfxFrame(x, y);
12033
12034     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12035       DrawLevelGraphicAnimation(x, y, graphic);
12036
12037     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12038         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12039       ResetRandomAnimationValue(x, y);
12040
12041     SetRandomAnimationValue(x, y);
12042
12043     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12044
12045     if (IS_INACTIVE(element))
12046     {
12047       if (IS_ANIMATED(graphic))
12048         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12049
12050       continue;
12051     }
12052
12053     // this may take place after moving, so 'element' may have changed
12054     if (IS_CHANGING(x, y) &&
12055         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12056     {
12057       int page = element_info[element].event_page_nr[CE_DELAY];
12058
12059       HandleElementChange(x, y, page);
12060
12061       element = Tile[x][y];
12062       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12063     }
12064
12065     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12066     {
12067       StartMoving(x, y);
12068
12069       element = Tile[x][y];
12070       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12071
12072       if (IS_ANIMATED(graphic) &&
12073           !IS_MOVING(x, y) &&
12074           !Stop[x][y])
12075         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12076
12077       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12078         TEST_DrawTwinkleOnField(x, y);
12079     }
12080     else if (element == EL_ACID)
12081     {
12082       if (!Stop[x][y])
12083         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12084     }
12085     else if ((element == EL_EXIT_OPEN ||
12086               element == EL_EM_EXIT_OPEN ||
12087               element == EL_SP_EXIT_OPEN ||
12088               element == EL_STEEL_EXIT_OPEN ||
12089               element == EL_EM_STEEL_EXIT_OPEN ||
12090               element == EL_SP_TERMINAL ||
12091               element == EL_SP_TERMINAL_ACTIVE ||
12092               element == EL_EXTRA_TIME ||
12093               element == EL_SHIELD_NORMAL ||
12094               element == EL_SHIELD_DEADLY) &&
12095              IS_ANIMATED(graphic))
12096       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12097     else if (IS_MOVING(x, y))
12098       ContinueMoving(x, y);
12099     else if (IS_ACTIVE_BOMB(element))
12100       CheckDynamite(x, y);
12101     else if (element == EL_AMOEBA_GROWING)
12102       AmoebaGrowing(x, y);
12103     else if (element == EL_AMOEBA_SHRINKING)
12104       AmoebaShrinking(x, y);
12105
12106 #if !USE_NEW_AMOEBA_CODE
12107     else if (IS_AMOEBALIVE(element))
12108       AmoebaReproduce(x, y);
12109 #endif
12110
12111     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12112       Life(x, y);
12113     else if (element == EL_EXIT_CLOSED)
12114       CheckExit(x, y);
12115     else if (element == EL_EM_EXIT_CLOSED)
12116       CheckExitEM(x, y);
12117     else if (element == EL_STEEL_EXIT_CLOSED)
12118       CheckExitSteel(x, y);
12119     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12120       CheckExitSteelEM(x, y);
12121     else if (element == EL_SP_EXIT_CLOSED)
12122       CheckExitSP(x, y);
12123     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12124              element == EL_EXPANDABLE_STEELWALL_GROWING)
12125       MauerWaechst(x, y);
12126     else if (element == EL_EXPANDABLE_WALL ||
12127              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12128              element == EL_EXPANDABLE_WALL_VERTICAL ||
12129              element == EL_EXPANDABLE_WALL_ANY ||
12130              element == EL_BD_EXPANDABLE_WALL)
12131       MauerAbleger(x, y);
12132     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12133              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12134              element == EL_EXPANDABLE_STEELWALL_ANY)
12135       MauerAblegerStahl(x, y);
12136     else if (element == EL_FLAMES)
12137       CheckForDragon(x, y);
12138     else if (element == EL_EXPLOSION)
12139       ; // drawing of correct explosion animation is handled separately
12140     else if (element == EL_ELEMENT_SNAPPING ||
12141              element == EL_DIAGONAL_SHRINKING ||
12142              element == EL_DIAGONAL_GROWING)
12143     {
12144       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12145
12146       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12147     }
12148     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12149       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12150
12151     if (IS_BELT_ACTIVE(element))
12152       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12153
12154     if (game.magic_wall_active)
12155     {
12156       int jx = local_player->jx, jy = local_player->jy;
12157
12158       // play the element sound at the position nearest to the player
12159       if ((element == EL_MAGIC_WALL_FULL ||
12160            element == EL_MAGIC_WALL_ACTIVE ||
12161            element == EL_MAGIC_WALL_EMPTYING ||
12162            element == EL_BD_MAGIC_WALL_FULL ||
12163            element == EL_BD_MAGIC_WALL_ACTIVE ||
12164            element == EL_BD_MAGIC_WALL_EMPTYING ||
12165            element == EL_DC_MAGIC_WALL_FULL ||
12166            element == EL_DC_MAGIC_WALL_ACTIVE ||
12167            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12168           ABS(x - jx) + ABS(y - jy) <
12169           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12170       {
12171         magic_wall_x = x;
12172         magic_wall_y = y;
12173       }
12174     }
12175   }
12176
12177 #if USE_NEW_AMOEBA_CODE
12178   // new experimental amoeba growth stuff
12179   if (!(FrameCounter % 8))
12180   {
12181     static unsigned int random = 1684108901;
12182
12183     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12184     {
12185       x = RND(lev_fieldx);
12186       y = RND(lev_fieldy);
12187       element = Tile[x][y];
12188
12189       if (!IS_PLAYER(x,y) &&
12190           (element == EL_EMPTY ||
12191            CAN_GROW_INTO(element) ||
12192            element == EL_QUICKSAND_EMPTY ||
12193            element == EL_QUICKSAND_FAST_EMPTY ||
12194            element == EL_ACID_SPLASH_LEFT ||
12195            element == EL_ACID_SPLASH_RIGHT))
12196       {
12197         if ((IN_LEV_FIELD(x, y-1) && Tile[x][y-1] == EL_AMOEBA_WET) ||
12198             (IN_LEV_FIELD(x-1, y) && Tile[x-1][y] == EL_AMOEBA_WET) ||
12199             (IN_LEV_FIELD(x+1, y) && Tile[x+1][y] == EL_AMOEBA_WET) ||
12200             (IN_LEV_FIELD(x, y+1) && Tile[x][y+1] == EL_AMOEBA_WET))
12201           Tile[x][y] = EL_AMOEBA_DROP;
12202       }
12203
12204       random = random * 129 + 1;
12205     }
12206   }
12207 #endif
12208
12209   game.explosions_delayed = FALSE;
12210
12211   SCAN_PLAYFIELD(x, y)
12212   {
12213     element = Tile[x][y];
12214
12215     if (ExplodeField[x][y])
12216       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12217     else if (element == EL_EXPLOSION)
12218       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12219
12220     ExplodeField[x][y] = EX_TYPE_NONE;
12221   }
12222
12223   game.explosions_delayed = TRUE;
12224
12225   if (game.magic_wall_active)
12226   {
12227     if (!(game.magic_wall_time_left % 4))
12228     {
12229       int element = Tile[magic_wall_x][magic_wall_y];
12230
12231       if (element == EL_BD_MAGIC_WALL_FULL ||
12232           element == EL_BD_MAGIC_WALL_ACTIVE ||
12233           element == EL_BD_MAGIC_WALL_EMPTYING)
12234         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12235       else if (element == EL_DC_MAGIC_WALL_FULL ||
12236                element == EL_DC_MAGIC_WALL_ACTIVE ||
12237                element == EL_DC_MAGIC_WALL_EMPTYING)
12238         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12239       else
12240         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12241     }
12242
12243     if (game.magic_wall_time_left > 0)
12244     {
12245       game.magic_wall_time_left--;
12246
12247       if (!game.magic_wall_time_left)
12248       {
12249         SCAN_PLAYFIELD(x, y)
12250         {
12251           element = Tile[x][y];
12252
12253           if (element == EL_MAGIC_WALL_ACTIVE ||
12254               element == EL_MAGIC_WALL_FULL)
12255           {
12256             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12257             TEST_DrawLevelField(x, y);
12258           }
12259           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12260                    element == EL_BD_MAGIC_WALL_FULL)
12261           {
12262             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12263             TEST_DrawLevelField(x, y);
12264           }
12265           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12266                    element == EL_DC_MAGIC_WALL_FULL)
12267           {
12268             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12269             TEST_DrawLevelField(x, y);
12270           }
12271         }
12272
12273         game.magic_wall_active = FALSE;
12274       }
12275     }
12276   }
12277
12278   if (game.light_time_left > 0)
12279   {
12280     game.light_time_left--;
12281
12282     if (game.light_time_left == 0)
12283       RedrawAllLightSwitchesAndInvisibleElements();
12284   }
12285
12286   if (game.timegate_time_left > 0)
12287   {
12288     game.timegate_time_left--;
12289
12290     if (game.timegate_time_left == 0)
12291       CloseAllOpenTimegates();
12292   }
12293
12294   if (game.lenses_time_left > 0)
12295   {
12296     game.lenses_time_left--;
12297
12298     if (game.lenses_time_left == 0)
12299       RedrawAllInvisibleElementsForLenses();
12300   }
12301
12302   if (game.magnify_time_left > 0)
12303   {
12304     game.magnify_time_left--;
12305
12306     if (game.magnify_time_left == 0)
12307       RedrawAllInvisibleElementsForMagnifier();
12308   }
12309
12310   for (i = 0; i < MAX_PLAYERS; i++)
12311   {
12312     struct PlayerInfo *player = &stored_player[i];
12313
12314     if (SHIELD_ON(player))
12315     {
12316       if (player->shield_deadly_time_left)
12317         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12318       else if (player->shield_normal_time_left)
12319         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12320     }
12321   }
12322
12323 #if USE_DELAYED_GFX_REDRAW
12324   SCAN_PLAYFIELD(x, y)
12325   {
12326     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12327     {
12328       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12329          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12330
12331       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12332         DrawLevelField(x, y);
12333
12334       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12335         DrawLevelFieldCrumbled(x, y);
12336
12337       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12338         DrawLevelFieldCrumbledNeighbours(x, y);
12339
12340       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12341         DrawTwinkleOnField(x, y);
12342     }
12343
12344     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12345   }
12346 #endif
12347
12348   DrawAllPlayers();
12349   PlayAllPlayersSound();
12350
12351   for (i = 0; i < MAX_PLAYERS; i++)
12352   {
12353     struct PlayerInfo *player = &stored_player[i];
12354
12355     if (player->show_envelope != 0 && (!player->active ||
12356                                        player->MovPos == 0))
12357     {
12358       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12359
12360       player->show_envelope = 0;
12361     }
12362   }
12363
12364   // use random number generator in every frame to make it less predictable
12365   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12366     RND(1);
12367
12368   mouse_action_last = mouse_action;
12369 }
12370
12371 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12372 {
12373   int min_x = x, min_y = y, max_x = x, max_y = y;
12374   int i;
12375
12376   for (i = 0; i < MAX_PLAYERS; i++)
12377   {
12378     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12379
12380     if (!stored_player[i].active || &stored_player[i] == player)
12381       continue;
12382
12383     min_x = MIN(min_x, jx);
12384     min_y = MIN(min_y, jy);
12385     max_x = MAX(max_x, jx);
12386     max_y = MAX(max_y, jy);
12387   }
12388
12389   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12390 }
12391
12392 static boolean AllPlayersInVisibleScreen(void)
12393 {
12394   int i;
12395
12396   for (i = 0; i < MAX_PLAYERS; i++)
12397   {
12398     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12399
12400     if (!stored_player[i].active)
12401       continue;
12402
12403     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12404       return FALSE;
12405   }
12406
12407   return TRUE;
12408 }
12409
12410 void ScrollLevel(int dx, int dy)
12411 {
12412   int scroll_offset = 2 * TILEX_VAR;
12413   int x, y;
12414
12415   BlitBitmap(drawto_field, drawto_field,
12416              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12417              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12418              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12419              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12420              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12421              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12422
12423   if (dx != 0)
12424   {
12425     x = (dx == 1 ? BX1 : BX2);
12426     for (y = BY1; y <= BY2; y++)
12427       DrawScreenField(x, y);
12428   }
12429
12430   if (dy != 0)
12431   {
12432     y = (dy == 1 ? BY1 : BY2);
12433     for (x = BX1; x <= BX2; x++)
12434       DrawScreenField(x, y);
12435   }
12436
12437   redraw_mask |= REDRAW_FIELD;
12438 }
12439
12440 static boolean canFallDown(struct PlayerInfo *player)
12441 {
12442   int jx = player->jx, jy = player->jy;
12443
12444   return (IN_LEV_FIELD(jx, jy + 1) &&
12445           (IS_FREE(jx, jy + 1) ||
12446            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12447           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12448           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12449 }
12450
12451 static boolean canPassField(int x, int y, int move_dir)
12452 {
12453   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12454   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12455   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12456   int nextx = x + dx;
12457   int nexty = y + dy;
12458   int element = Tile[x][y];
12459
12460   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12461           !CAN_MOVE(element) &&
12462           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12463           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12464           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12465 }
12466
12467 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12468 {
12469   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12470   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12471   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12472   int newx = x + dx;
12473   int newy = y + dy;
12474
12475   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12476           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12477           (IS_DIGGABLE(Tile[newx][newy]) ||
12478            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12479            canPassField(newx, newy, move_dir)));
12480 }
12481
12482 static void CheckGravityMovement(struct PlayerInfo *player)
12483 {
12484   if (player->gravity && !player->programmed_action)
12485   {
12486     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12487     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12488     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12489     int jx = player->jx, jy = player->jy;
12490     boolean player_is_moving_to_valid_field =
12491       (!player_is_snapping &&
12492        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12493         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12494     boolean player_can_fall_down = canFallDown(player);
12495
12496     if (player_can_fall_down &&
12497         !player_is_moving_to_valid_field)
12498       player->programmed_action = MV_DOWN;
12499   }
12500 }
12501
12502 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12503 {
12504   return CheckGravityMovement(player);
12505
12506   if (player->gravity && !player->programmed_action)
12507   {
12508     int jx = player->jx, jy = player->jy;
12509     boolean field_under_player_is_free =
12510       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12511     boolean player_is_standing_on_valid_field =
12512       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12513        (IS_WALKABLE(Tile[jx][jy]) &&
12514         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12515
12516     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12517       player->programmed_action = MV_DOWN;
12518   }
12519 }
12520
12521 /*
12522   MovePlayerOneStep()
12523   -----------------------------------------------------------------------------
12524   dx, dy:               direction (non-diagonal) to try to move the player to
12525   real_dx, real_dy:     direction as read from input device (can be diagonal)
12526 */
12527
12528 boolean MovePlayerOneStep(struct PlayerInfo *player,
12529                           int dx, int dy, int real_dx, int real_dy)
12530 {
12531   int jx = player->jx, jy = player->jy;
12532   int new_jx = jx + dx, new_jy = jy + dy;
12533   int can_move;
12534   boolean player_can_move = !player->cannot_move;
12535
12536   if (!player->active || (!dx && !dy))
12537     return MP_NO_ACTION;
12538
12539   player->MovDir = (dx < 0 ? MV_LEFT :
12540                     dx > 0 ? MV_RIGHT :
12541                     dy < 0 ? MV_UP :
12542                     dy > 0 ? MV_DOWN :  MV_NONE);
12543
12544   if (!IN_LEV_FIELD(new_jx, new_jy))
12545     return MP_NO_ACTION;
12546
12547   if (!player_can_move)
12548   {
12549     if (player->MovPos == 0)
12550     {
12551       player->is_moving = FALSE;
12552       player->is_digging = FALSE;
12553       player->is_collecting = FALSE;
12554       player->is_snapping = FALSE;
12555       player->is_pushing = FALSE;
12556     }
12557   }
12558
12559   if (!network.enabled && game.centered_player_nr == -1 &&
12560       !AllPlayersInSight(player, new_jx, new_jy))
12561     return MP_NO_ACTION;
12562
12563   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12564   if (can_move != MP_MOVING)
12565     return can_move;
12566
12567   // check if DigField() has caused relocation of the player
12568   if (player->jx != jx || player->jy != jy)
12569     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12570
12571   StorePlayer[jx][jy] = 0;
12572   player->last_jx = jx;
12573   player->last_jy = jy;
12574   player->jx = new_jx;
12575   player->jy = new_jy;
12576   StorePlayer[new_jx][new_jy] = player->element_nr;
12577
12578   if (player->move_delay_value_next != -1)
12579   {
12580     player->move_delay_value = player->move_delay_value_next;
12581     player->move_delay_value_next = -1;
12582   }
12583
12584   player->MovPos =
12585     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12586
12587   player->step_counter++;
12588
12589   PlayerVisit[jx][jy] = FrameCounter;
12590
12591   player->is_moving = TRUE;
12592
12593 #if 1
12594   // should better be called in MovePlayer(), but this breaks some tapes
12595   ScrollPlayer(player, SCROLL_INIT);
12596 #endif
12597
12598   return MP_MOVING;
12599 }
12600
12601 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12602 {
12603   int jx = player->jx, jy = player->jy;
12604   int old_jx = jx, old_jy = jy;
12605   int moved = MP_NO_ACTION;
12606
12607   if (!player->active)
12608     return FALSE;
12609
12610   if (!dx && !dy)
12611   {
12612     if (player->MovPos == 0)
12613     {
12614       player->is_moving = FALSE;
12615       player->is_digging = FALSE;
12616       player->is_collecting = FALSE;
12617       player->is_snapping = FALSE;
12618       player->is_pushing = FALSE;
12619     }
12620
12621     return FALSE;
12622   }
12623
12624   if (player->move_delay > 0)
12625     return FALSE;
12626
12627   player->move_delay = -1;              // set to "uninitialized" value
12628
12629   // store if player is automatically moved to next field
12630   player->is_auto_moving = (player->programmed_action != MV_NONE);
12631
12632   // remove the last programmed player action
12633   player->programmed_action = 0;
12634
12635   if (player->MovPos)
12636   {
12637     // should only happen if pre-1.2 tape recordings are played
12638     // this is only for backward compatibility
12639
12640     int original_move_delay_value = player->move_delay_value;
12641
12642 #if DEBUG
12643     Debug("game:playing:MovePlayer",
12644           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12645           tape.counter);
12646 #endif
12647
12648     // scroll remaining steps with finest movement resolution
12649     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12650
12651     while (player->MovPos)
12652     {
12653       ScrollPlayer(player, SCROLL_GO_ON);
12654       ScrollScreen(NULL, SCROLL_GO_ON);
12655
12656       AdvanceFrameAndPlayerCounters(player->index_nr);
12657
12658       DrawAllPlayers();
12659       BackToFront_WithFrameDelay(0);
12660     }
12661
12662     player->move_delay_value = original_move_delay_value;
12663   }
12664
12665   player->is_active = FALSE;
12666
12667   if (player->last_move_dir & MV_HORIZONTAL)
12668   {
12669     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12670       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12671   }
12672   else
12673   {
12674     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12675       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12676   }
12677
12678   if (!moved && !player->is_active)
12679   {
12680     player->is_moving = FALSE;
12681     player->is_digging = FALSE;
12682     player->is_collecting = FALSE;
12683     player->is_snapping = FALSE;
12684     player->is_pushing = FALSE;
12685   }
12686
12687   jx = player->jx;
12688   jy = player->jy;
12689
12690   if (moved & MP_MOVING && !ScreenMovPos &&
12691       (player->index_nr == game.centered_player_nr ||
12692        game.centered_player_nr == -1))
12693   {
12694     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12695
12696     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12697     {
12698       // actual player has left the screen -- scroll in that direction
12699       if (jx != old_jx)         // player has moved horizontally
12700         scroll_x += (jx - old_jx);
12701       else                      // player has moved vertically
12702         scroll_y += (jy - old_jy);
12703     }
12704     else
12705     {
12706       int offset_raw = game.scroll_delay_value;
12707
12708       if (jx != old_jx)         // player has moved horizontally
12709       {
12710         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12711         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12712         int new_scroll_x = jx - MIDPOSX + offset_x;
12713
12714         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12715             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12716           scroll_x = new_scroll_x;
12717
12718         // don't scroll over playfield boundaries
12719         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12720
12721         // don't scroll more than one field at a time
12722         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12723
12724         // don't scroll against the player's moving direction
12725         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12726             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12727           scroll_x = old_scroll_x;
12728       }
12729       else                      // player has moved vertically
12730       {
12731         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12732         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12733         int new_scroll_y = jy - MIDPOSY + offset_y;
12734
12735         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12736             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12737           scroll_y = new_scroll_y;
12738
12739         // don't scroll over playfield boundaries
12740         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
12741
12742         // don't scroll more than one field at a time
12743         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12744
12745         // don't scroll against the player's moving direction
12746         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12747             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12748           scroll_y = old_scroll_y;
12749       }
12750     }
12751
12752     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12753     {
12754       if (!network.enabled && game.centered_player_nr == -1 &&
12755           !AllPlayersInVisibleScreen())
12756       {
12757         scroll_x = old_scroll_x;
12758         scroll_y = old_scroll_y;
12759       }
12760       else
12761       {
12762         ScrollScreen(player, SCROLL_INIT);
12763         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12764       }
12765     }
12766   }
12767
12768   player->StepFrame = 0;
12769
12770   if (moved & MP_MOVING)
12771   {
12772     if (old_jx != jx && old_jy == jy)
12773       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12774     else if (old_jx == jx && old_jy != jy)
12775       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12776
12777     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12778
12779     player->last_move_dir = player->MovDir;
12780     player->is_moving = TRUE;
12781     player->is_snapping = FALSE;
12782     player->is_switching = FALSE;
12783     player->is_dropping = FALSE;
12784     player->is_dropping_pressed = FALSE;
12785     player->drop_pressed_delay = 0;
12786
12787 #if 0
12788     // should better be called here than above, but this breaks some tapes
12789     ScrollPlayer(player, SCROLL_INIT);
12790 #endif
12791   }
12792   else
12793   {
12794     CheckGravityMovementWhenNotMoving(player);
12795
12796     player->is_moving = FALSE;
12797
12798     /* at this point, the player is allowed to move, but cannot move right now
12799        (e.g. because of something blocking the way) -- ensure that the player
12800        is also allowed to move in the next frame (in old versions before 3.1.1,
12801        the player was forced to wait again for eight frames before next try) */
12802
12803     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12804       player->move_delay = 0;   // allow direct movement in the next frame
12805   }
12806
12807   if (player->move_delay == -1)         // not yet initialized by DigField()
12808     player->move_delay = player->move_delay_value;
12809
12810   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12811   {
12812     TestIfPlayerTouchesBadThing(jx, jy);
12813     TestIfPlayerTouchesCustomElement(jx, jy);
12814   }
12815
12816   if (!player->active)
12817     RemovePlayer(player);
12818
12819   return moved;
12820 }
12821
12822 void ScrollPlayer(struct PlayerInfo *player, int mode)
12823 {
12824   int jx = player->jx, jy = player->jy;
12825   int last_jx = player->last_jx, last_jy = player->last_jy;
12826   int move_stepsize = TILEX / player->move_delay_value;
12827
12828   if (!player->active)
12829     return;
12830
12831   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
12832     return;
12833
12834   if (mode == SCROLL_INIT)
12835   {
12836     player->actual_frame_counter = FrameCounter;
12837     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12838
12839     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12840         Tile[last_jx][last_jy] == EL_EMPTY)
12841     {
12842       int last_field_block_delay = 0;   // start with no blocking at all
12843       int block_delay_adjustment = player->block_delay_adjustment;
12844
12845       // if player blocks last field, add delay for exactly one move
12846       if (player->block_last_field)
12847       {
12848         last_field_block_delay += player->move_delay_value;
12849
12850         // when blocking enabled, prevent moving up despite gravity
12851         if (player->gravity && player->MovDir == MV_UP)
12852           block_delay_adjustment = -1;
12853       }
12854
12855       // add block delay adjustment (also possible when not blocking)
12856       last_field_block_delay += block_delay_adjustment;
12857
12858       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12859       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12860     }
12861
12862     if (player->MovPos != 0)    // player has not yet reached destination
12863       return;
12864   }
12865   else if (!FrameReached(&player->actual_frame_counter, 1))
12866     return;
12867
12868   if (player->MovPos != 0)
12869   {
12870     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12871     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12872
12873     // before DrawPlayer() to draw correct player graphic for this case
12874     if (player->MovPos == 0)
12875       CheckGravityMovement(player);
12876   }
12877
12878   if (player->MovPos == 0)      // player reached destination field
12879   {
12880     if (player->move_delay_reset_counter > 0)
12881     {
12882       player->move_delay_reset_counter--;
12883
12884       if (player->move_delay_reset_counter == 0)
12885       {
12886         // continue with normal speed after quickly moving through gate
12887         HALVE_PLAYER_SPEED(player);
12888
12889         // be able to make the next move without delay
12890         player->move_delay = 0;
12891       }
12892     }
12893
12894     player->last_jx = jx;
12895     player->last_jy = jy;
12896
12897     if (Tile[jx][jy] == EL_EXIT_OPEN ||
12898         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
12899         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
12900         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
12901         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12902         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12903         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
12904         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
12905     {
12906       ExitPlayer(player);
12907
12908       if (game.players_still_needed == 0 &&
12909           (game.friends_still_needed == 0 ||
12910            IS_SP_ELEMENT(Tile[jx][jy])))
12911         LevelSolved();
12912     }
12913
12914     // this breaks one level: "machine", level 000
12915     {
12916       int move_direction = player->MovDir;
12917       int enter_side = MV_DIR_OPPOSITE(move_direction);
12918       int leave_side = move_direction;
12919       int old_jx = last_jx;
12920       int old_jy = last_jy;
12921       int old_element = Tile[old_jx][old_jy];
12922       int new_element = Tile[jx][jy];
12923
12924       if (IS_CUSTOM_ELEMENT(old_element))
12925         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12926                                    CE_LEFT_BY_PLAYER,
12927                                    player->index_bit, leave_side);
12928
12929       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12930                                           CE_PLAYER_LEAVES_X,
12931                                           player->index_bit, leave_side);
12932
12933       if (IS_CUSTOM_ELEMENT(new_element))
12934         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12935                                    player->index_bit, enter_side);
12936
12937       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12938                                           CE_PLAYER_ENTERS_X,
12939                                           player->index_bit, enter_side);
12940
12941       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12942                                         CE_MOVE_OF_X, move_direction);
12943     }
12944
12945     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12946     {
12947       TestIfPlayerTouchesBadThing(jx, jy);
12948       TestIfPlayerTouchesCustomElement(jx, jy);
12949
12950       /* needed because pushed element has not yet reached its destination,
12951          so it would trigger a change event at its previous field location */
12952       if (!player->is_pushing)
12953         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
12954
12955       if (!player->active)
12956         RemovePlayer(player);
12957     }
12958
12959     if (!game.LevelSolved && level.use_step_counter)
12960     {
12961       int i;
12962
12963       TimePlayed++;
12964
12965       if (TimeLeft > 0)
12966       {
12967         TimeLeft--;
12968
12969         if (TimeLeft <= 10 && setup.time_limit)
12970           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12971
12972         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12973
12974         DisplayGameControlValues();
12975
12976         if (!TimeLeft && setup.time_limit)
12977           for (i = 0; i < MAX_PLAYERS; i++)
12978             KillPlayer(&stored_player[i]);
12979       }
12980       else if (game.no_time_limit && !game.all_players_gone)
12981       {
12982         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12983
12984         DisplayGameControlValues();
12985       }
12986     }
12987
12988     if (tape.single_step && tape.recording && !tape.pausing &&
12989         !player->programmed_action)
12990       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12991
12992     if (!player->programmed_action)
12993       CheckSaveEngineSnapshot(player);
12994   }
12995 }
12996
12997 void ScrollScreen(struct PlayerInfo *player, int mode)
12998 {
12999   static unsigned int screen_frame_counter = 0;
13000
13001   if (mode == SCROLL_INIT)
13002   {
13003     // set scrolling step size according to actual player's moving speed
13004     ScrollStepSize = TILEX / player->move_delay_value;
13005
13006     screen_frame_counter = FrameCounter;
13007     ScreenMovDir = player->MovDir;
13008     ScreenMovPos = player->MovPos;
13009     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13010     return;
13011   }
13012   else if (!FrameReached(&screen_frame_counter, 1))
13013     return;
13014
13015   if (ScreenMovPos)
13016   {
13017     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13018     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13019     redraw_mask |= REDRAW_FIELD;
13020   }
13021   else
13022     ScreenMovDir = MV_NONE;
13023 }
13024
13025 void TestIfPlayerTouchesCustomElement(int x, int y)
13026 {
13027   static int xy[4][2] =
13028   {
13029     { 0, -1 },
13030     { -1, 0 },
13031     { +1, 0 },
13032     { 0, +1 }
13033   };
13034   static int trigger_sides[4][2] =
13035   {
13036     // center side       border side
13037     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13038     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13039     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13040     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13041   };
13042   static int touch_dir[4] =
13043   {
13044     MV_LEFT | MV_RIGHT,
13045     MV_UP   | MV_DOWN,
13046     MV_UP   | MV_DOWN,
13047     MV_LEFT | MV_RIGHT
13048   };
13049   int center_element = Tile[x][y];      // should always be non-moving!
13050   int i;
13051
13052   for (i = 0; i < NUM_DIRECTIONS; i++)
13053   {
13054     int xx = x + xy[i][0];
13055     int yy = y + xy[i][1];
13056     int center_side = trigger_sides[i][0];
13057     int border_side = trigger_sides[i][1];
13058     int border_element;
13059
13060     if (!IN_LEV_FIELD(xx, yy))
13061       continue;
13062
13063     if (IS_PLAYER(x, y))                // player found at center element
13064     {
13065       struct PlayerInfo *player = PLAYERINFO(x, y);
13066
13067       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13068         border_element = Tile[xx][yy];          // may be moving!
13069       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13070         border_element = Tile[xx][yy];
13071       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13072         border_element = MovingOrBlocked2Element(xx, yy);
13073       else
13074         continue;               // center and border element do not touch
13075
13076       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13077                                  player->index_bit, border_side);
13078       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13079                                           CE_PLAYER_TOUCHES_X,
13080                                           player->index_bit, border_side);
13081
13082       {
13083         /* use player element that is initially defined in the level playfield,
13084            not the player element that corresponds to the runtime player number
13085            (example: a level that contains EL_PLAYER_3 as the only player would
13086            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13087         int player_element = PLAYERINFO(x, y)->initial_element;
13088
13089         CheckElementChangeBySide(xx, yy, border_element, player_element,
13090                                  CE_TOUCHING_X, border_side);
13091       }
13092     }
13093     else if (IS_PLAYER(xx, yy))         // player found at border element
13094     {
13095       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13096
13097       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13098       {
13099         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13100           continue;             // center and border element do not touch
13101       }
13102
13103       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13104                                  player->index_bit, center_side);
13105       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13106                                           CE_PLAYER_TOUCHES_X,
13107                                           player->index_bit, center_side);
13108
13109       {
13110         /* use player element that is initially defined in the level playfield,
13111            not the player element that corresponds to the runtime player number
13112            (example: a level that contains EL_PLAYER_3 as the only player would
13113            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13114         int player_element = PLAYERINFO(xx, yy)->initial_element;
13115
13116         CheckElementChangeBySide(x, y, center_element, player_element,
13117                                  CE_TOUCHING_X, center_side);
13118       }
13119
13120       break;
13121     }
13122   }
13123 }
13124
13125 void TestIfElementTouchesCustomElement(int x, int y)
13126 {
13127   static int xy[4][2] =
13128   {
13129     { 0, -1 },
13130     { -1, 0 },
13131     { +1, 0 },
13132     { 0, +1 }
13133   };
13134   static int trigger_sides[4][2] =
13135   {
13136     // center side      border side
13137     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13138     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13139     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13140     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13141   };
13142   static int touch_dir[4] =
13143   {
13144     MV_LEFT | MV_RIGHT,
13145     MV_UP   | MV_DOWN,
13146     MV_UP   | MV_DOWN,
13147     MV_LEFT | MV_RIGHT
13148   };
13149   boolean change_center_element = FALSE;
13150   int center_element = Tile[x][y];      // should always be non-moving!
13151   int border_element_old[NUM_DIRECTIONS];
13152   int i;
13153
13154   for (i = 0; i < NUM_DIRECTIONS; i++)
13155   {
13156     int xx = x + xy[i][0];
13157     int yy = y + xy[i][1];
13158     int border_element;
13159
13160     border_element_old[i] = -1;
13161
13162     if (!IN_LEV_FIELD(xx, yy))
13163       continue;
13164
13165     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13166       border_element = Tile[xx][yy];    // may be moving!
13167     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13168       border_element = Tile[xx][yy];
13169     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13170       border_element = MovingOrBlocked2Element(xx, yy);
13171     else
13172       continue;                 // center and border element do not touch
13173
13174     border_element_old[i] = border_element;
13175   }
13176
13177   for (i = 0; i < NUM_DIRECTIONS; i++)
13178   {
13179     int xx = x + xy[i][0];
13180     int yy = y + xy[i][1];
13181     int center_side = trigger_sides[i][0];
13182     int border_element = border_element_old[i];
13183
13184     if (border_element == -1)
13185       continue;
13186
13187     // check for change of border element
13188     CheckElementChangeBySide(xx, yy, border_element, center_element,
13189                              CE_TOUCHING_X, center_side);
13190
13191     // (center element cannot be player, so we dont have to check this here)
13192   }
13193
13194   for (i = 0; i < NUM_DIRECTIONS; i++)
13195   {
13196     int xx = x + xy[i][0];
13197     int yy = y + xy[i][1];
13198     int border_side = trigger_sides[i][1];
13199     int border_element = border_element_old[i];
13200
13201     if (border_element == -1)
13202       continue;
13203
13204     // check for change of center element (but change it only once)
13205     if (!change_center_element)
13206       change_center_element =
13207         CheckElementChangeBySide(x, y, center_element, border_element,
13208                                  CE_TOUCHING_X, border_side);
13209
13210     if (IS_PLAYER(xx, yy))
13211     {
13212       /* use player element that is initially defined in the level playfield,
13213          not the player element that corresponds to the runtime player number
13214          (example: a level that contains EL_PLAYER_3 as the only player would
13215          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13216       int player_element = PLAYERINFO(xx, yy)->initial_element;
13217
13218       CheckElementChangeBySide(x, y, center_element, player_element,
13219                                CE_TOUCHING_X, border_side);
13220     }
13221   }
13222 }
13223
13224 void TestIfElementHitsCustomElement(int x, int y, int direction)
13225 {
13226   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13227   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13228   int hitx = x + dx, hity = y + dy;
13229   int hitting_element = Tile[x][y];
13230   int touched_element;
13231
13232   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13233     return;
13234
13235   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13236                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13237
13238   if (IN_LEV_FIELD(hitx, hity))
13239   {
13240     int opposite_direction = MV_DIR_OPPOSITE(direction);
13241     int hitting_side = direction;
13242     int touched_side = opposite_direction;
13243     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13244                           MovDir[hitx][hity] != direction ||
13245                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13246
13247     object_hit = TRUE;
13248
13249     if (object_hit)
13250     {
13251       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13252                                CE_HITTING_X, touched_side);
13253
13254       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13255                                CE_HIT_BY_X, hitting_side);
13256
13257       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13258                                CE_HIT_BY_SOMETHING, opposite_direction);
13259
13260       if (IS_PLAYER(hitx, hity))
13261       {
13262         /* use player element that is initially defined in the level playfield,
13263            not the player element that corresponds to the runtime player number
13264            (example: a level that contains EL_PLAYER_3 as the only player would
13265            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13266         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13267
13268         CheckElementChangeBySide(x, y, hitting_element, player_element,
13269                                  CE_HITTING_X, touched_side);
13270       }
13271     }
13272   }
13273
13274   // "hitting something" is also true when hitting the playfield border
13275   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13276                            CE_HITTING_SOMETHING, direction);
13277 }
13278
13279 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13280 {
13281   int i, kill_x = -1, kill_y = -1;
13282
13283   int bad_element = -1;
13284   static int test_xy[4][2] =
13285   {
13286     { 0, -1 },
13287     { -1, 0 },
13288     { +1, 0 },
13289     { 0, +1 }
13290   };
13291   static int test_dir[4] =
13292   {
13293     MV_UP,
13294     MV_LEFT,
13295     MV_RIGHT,
13296     MV_DOWN
13297   };
13298
13299   for (i = 0; i < NUM_DIRECTIONS; i++)
13300   {
13301     int test_x, test_y, test_move_dir, test_element;
13302
13303     test_x = good_x + test_xy[i][0];
13304     test_y = good_y + test_xy[i][1];
13305
13306     if (!IN_LEV_FIELD(test_x, test_y))
13307       continue;
13308
13309     test_move_dir =
13310       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13311
13312     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13313
13314     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13315        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13316     */
13317     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13318         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13319     {
13320       kill_x = test_x;
13321       kill_y = test_y;
13322       bad_element = test_element;
13323
13324       break;
13325     }
13326   }
13327
13328   if (kill_x != -1 || kill_y != -1)
13329   {
13330     if (IS_PLAYER(good_x, good_y))
13331     {
13332       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13333
13334       if (player->shield_deadly_time_left > 0 &&
13335           !IS_INDESTRUCTIBLE(bad_element))
13336         Bang(kill_x, kill_y);
13337       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13338         KillPlayer(player);
13339     }
13340     else
13341       Bang(good_x, good_y);
13342   }
13343 }
13344
13345 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13346 {
13347   int i, kill_x = -1, kill_y = -1;
13348   int bad_element = Tile[bad_x][bad_y];
13349   static int test_xy[4][2] =
13350   {
13351     { 0, -1 },
13352     { -1, 0 },
13353     { +1, 0 },
13354     { 0, +1 }
13355   };
13356   static int touch_dir[4] =
13357   {
13358     MV_LEFT | MV_RIGHT,
13359     MV_UP   | MV_DOWN,
13360     MV_UP   | MV_DOWN,
13361     MV_LEFT | MV_RIGHT
13362   };
13363   static int test_dir[4] =
13364   {
13365     MV_UP,
13366     MV_LEFT,
13367     MV_RIGHT,
13368     MV_DOWN
13369   };
13370
13371   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13372     return;
13373
13374   for (i = 0; i < NUM_DIRECTIONS; i++)
13375   {
13376     int test_x, test_y, test_move_dir, test_element;
13377
13378     test_x = bad_x + test_xy[i][0];
13379     test_y = bad_y + test_xy[i][1];
13380
13381     if (!IN_LEV_FIELD(test_x, test_y))
13382       continue;
13383
13384     test_move_dir =
13385       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13386
13387     test_element = Tile[test_x][test_y];
13388
13389     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13390        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13391     */
13392     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13393         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13394     {
13395       // good thing is player or penguin that does not move away
13396       if (IS_PLAYER(test_x, test_y))
13397       {
13398         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13399
13400         if (bad_element == EL_ROBOT && player->is_moving)
13401           continue;     // robot does not kill player if he is moving
13402
13403         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13404         {
13405           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13406             continue;           // center and border element do not touch
13407         }
13408
13409         kill_x = test_x;
13410         kill_y = test_y;
13411
13412         break;
13413       }
13414       else if (test_element == EL_PENGUIN)
13415       {
13416         kill_x = test_x;
13417         kill_y = test_y;
13418
13419         break;
13420       }
13421     }
13422   }
13423
13424   if (kill_x != -1 || kill_y != -1)
13425   {
13426     if (IS_PLAYER(kill_x, kill_y))
13427     {
13428       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13429
13430       if (player->shield_deadly_time_left > 0 &&
13431           !IS_INDESTRUCTIBLE(bad_element))
13432         Bang(bad_x, bad_y);
13433       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13434         KillPlayer(player);
13435     }
13436     else
13437       Bang(kill_x, kill_y);
13438   }
13439 }
13440
13441 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13442 {
13443   int bad_element = Tile[bad_x][bad_y];
13444   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13445   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13446   int test_x = bad_x + dx, test_y = bad_y + dy;
13447   int test_move_dir, test_element;
13448   int kill_x = -1, kill_y = -1;
13449
13450   if (!IN_LEV_FIELD(test_x, test_y))
13451     return;
13452
13453   test_move_dir =
13454     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13455
13456   test_element = Tile[test_x][test_y];
13457
13458   if (test_move_dir != bad_move_dir)
13459   {
13460     // good thing can be player or penguin that does not move away
13461     if (IS_PLAYER(test_x, test_y))
13462     {
13463       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13464
13465       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13466          player as being hit when he is moving towards the bad thing, because
13467          the "get hit by" condition would be lost after the player stops) */
13468       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13469         return;         // player moves away from bad thing
13470
13471       kill_x = test_x;
13472       kill_y = test_y;
13473     }
13474     else if (test_element == EL_PENGUIN)
13475     {
13476       kill_x = test_x;
13477       kill_y = test_y;
13478     }
13479   }
13480
13481   if (kill_x != -1 || kill_y != -1)
13482   {
13483     if (IS_PLAYER(kill_x, kill_y))
13484     {
13485       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13486
13487       if (player->shield_deadly_time_left > 0 &&
13488           !IS_INDESTRUCTIBLE(bad_element))
13489         Bang(bad_x, bad_y);
13490       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13491         KillPlayer(player);
13492     }
13493     else
13494       Bang(kill_x, kill_y);
13495   }
13496 }
13497
13498 void TestIfPlayerTouchesBadThing(int x, int y)
13499 {
13500   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13501 }
13502
13503 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13504 {
13505   TestIfGoodThingHitsBadThing(x, y, move_dir);
13506 }
13507
13508 void TestIfBadThingTouchesPlayer(int x, int y)
13509 {
13510   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13511 }
13512
13513 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13514 {
13515   TestIfBadThingHitsGoodThing(x, y, move_dir);
13516 }
13517
13518 void TestIfFriendTouchesBadThing(int x, int y)
13519 {
13520   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13521 }
13522
13523 void TestIfBadThingTouchesFriend(int x, int y)
13524 {
13525   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13526 }
13527
13528 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13529 {
13530   int i, kill_x = bad_x, kill_y = bad_y;
13531   static int xy[4][2] =
13532   {
13533     { 0, -1 },
13534     { -1, 0 },
13535     { +1, 0 },
13536     { 0, +1 }
13537   };
13538
13539   for (i = 0; i < NUM_DIRECTIONS; i++)
13540   {
13541     int x, y, element;
13542
13543     x = bad_x + xy[i][0];
13544     y = bad_y + xy[i][1];
13545     if (!IN_LEV_FIELD(x, y))
13546       continue;
13547
13548     element = Tile[x][y];
13549     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13550         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13551     {
13552       kill_x = x;
13553       kill_y = y;
13554       break;
13555     }
13556   }
13557
13558   if (kill_x != bad_x || kill_y != bad_y)
13559     Bang(bad_x, bad_y);
13560 }
13561
13562 void KillPlayer(struct PlayerInfo *player)
13563 {
13564   int jx = player->jx, jy = player->jy;
13565
13566   if (!player->active)
13567     return;
13568
13569 #if 0
13570   Debug("game:playing:KillPlayer",
13571         "0: killed == %d, active == %d, reanimated == %d",
13572         player->killed, player->active, player->reanimated);
13573 #endif
13574
13575   /* the following code was introduced to prevent an infinite loop when calling
13576      -> Bang()
13577      -> CheckTriggeredElementChangeExt()
13578      -> ExecuteCustomElementAction()
13579      -> KillPlayer()
13580      -> (infinitely repeating the above sequence of function calls)
13581      which occurs when killing the player while having a CE with the setting
13582      "kill player X when explosion of <player X>"; the solution using a new
13583      field "player->killed" was chosen for backwards compatibility, although
13584      clever use of the fields "player->active" etc. would probably also work */
13585 #if 1
13586   if (player->killed)
13587     return;
13588 #endif
13589
13590   player->killed = TRUE;
13591
13592   // remove accessible field at the player's position
13593   Tile[jx][jy] = EL_EMPTY;
13594
13595   // deactivate shield (else Bang()/Explode() would not work right)
13596   player->shield_normal_time_left = 0;
13597   player->shield_deadly_time_left = 0;
13598
13599 #if 0
13600   Debug("game:playing:KillPlayer",
13601         "1: killed == %d, active == %d, reanimated == %d",
13602         player->killed, player->active, player->reanimated);
13603 #endif
13604
13605   Bang(jx, jy);
13606
13607 #if 0
13608   Debug("game:playing:KillPlayer",
13609         "2: killed == %d, active == %d, reanimated == %d",
13610         player->killed, player->active, player->reanimated);
13611 #endif
13612
13613   if (player->reanimated)       // killed player may have been reanimated
13614     player->killed = player->reanimated = FALSE;
13615   else
13616     BuryPlayer(player);
13617 }
13618
13619 static void KillPlayerUnlessEnemyProtected(int x, int y)
13620 {
13621   if (!PLAYER_ENEMY_PROTECTED(x, y))
13622     KillPlayer(PLAYERINFO(x, y));
13623 }
13624
13625 static void KillPlayerUnlessExplosionProtected(int x, int y)
13626 {
13627   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13628     KillPlayer(PLAYERINFO(x, y));
13629 }
13630
13631 void BuryPlayer(struct PlayerInfo *player)
13632 {
13633   int jx = player->jx, jy = player->jy;
13634
13635   if (!player->active)
13636     return;
13637
13638   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13639   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13640
13641   RemovePlayer(player);
13642
13643   player->buried = TRUE;
13644
13645   if (game.all_players_gone)
13646     game.GameOver = TRUE;
13647 }
13648
13649 void RemovePlayer(struct PlayerInfo *player)
13650 {
13651   int jx = player->jx, jy = player->jy;
13652   int i, found = FALSE;
13653
13654   player->present = FALSE;
13655   player->active = FALSE;
13656
13657   // required for some CE actions (even if the player is not active anymore)
13658   player->MovPos = 0;
13659
13660   if (!ExplodeField[jx][jy])
13661     StorePlayer[jx][jy] = 0;
13662
13663   if (player->is_moving)
13664     TEST_DrawLevelField(player->last_jx, player->last_jy);
13665
13666   for (i = 0; i < MAX_PLAYERS; i++)
13667     if (stored_player[i].active)
13668       found = TRUE;
13669
13670   if (!found)
13671   {
13672     game.all_players_gone = TRUE;
13673     game.GameOver = TRUE;
13674   }
13675
13676   game.exit_x = game.robot_wheel_x = jx;
13677   game.exit_y = game.robot_wheel_y = jy;
13678 }
13679
13680 void ExitPlayer(struct PlayerInfo *player)
13681 {
13682   DrawPlayer(player);   // needed here only to cleanup last field
13683   RemovePlayer(player);
13684
13685   if (game.players_still_needed > 0)
13686     game.players_still_needed--;
13687 }
13688
13689 static void setFieldForSnapping(int x, int y, int element, int direction)
13690 {
13691   struct ElementInfo *ei = &element_info[element];
13692   int direction_bit = MV_DIR_TO_BIT(direction);
13693   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13694   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13695                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13696
13697   Tile[x][y] = EL_ELEMENT_SNAPPING;
13698   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13699
13700   ResetGfxAnimation(x, y);
13701
13702   GfxElement[x][y] = element;
13703   GfxAction[x][y] = action;
13704   GfxDir[x][y] = direction;
13705   GfxFrame[x][y] = -1;
13706 }
13707
13708 /*
13709   =============================================================================
13710   checkDiagonalPushing()
13711   -----------------------------------------------------------------------------
13712   check if diagonal input device direction results in pushing of object
13713   (by checking if the alternative direction is walkable, diggable, ...)
13714   =============================================================================
13715 */
13716
13717 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13718                                     int x, int y, int real_dx, int real_dy)
13719 {
13720   int jx, jy, dx, dy, xx, yy;
13721
13722   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13723     return TRUE;
13724
13725   // diagonal direction: check alternative direction
13726   jx = player->jx;
13727   jy = player->jy;
13728   dx = x - jx;
13729   dy = y - jy;
13730   xx = jx + (dx == 0 ? real_dx : 0);
13731   yy = jy + (dy == 0 ? real_dy : 0);
13732
13733   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
13734 }
13735
13736 /*
13737   =============================================================================
13738   DigField()
13739   -----------------------------------------------------------------------------
13740   x, y:                 field next to player (non-diagonal) to try to dig to
13741   real_dx, real_dy:     direction as read from input device (can be diagonal)
13742   =============================================================================
13743 */
13744
13745 static int DigField(struct PlayerInfo *player,
13746                     int oldx, int oldy, int x, int y,
13747                     int real_dx, int real_dy, int mode)
13748 {
13749   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13750   boolean player_was_pushing = player->is_pushing;
13751   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13752   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13753   int jx = oldx, jy = oldy;
13754   int dx = x - jx, dy = y - jy;
13755   int nextx = x + dx, nexty = y + dy;
13756   int move_direction = (dx == -1 ? MV_LEFT  :
13757                         dx == +1 ? MV_RIGHT :
13758                         dy == -1 ? MV_UP    :
13759                         dy == +1 ? MV_DOWN  : MV_NONE);
13760   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13761   int dig_side = MV_DIR_OPPOSITE(move_direction);
13762   int old_element = Tile[jx][jy];
13763   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13764   int collect_count;
13765
13766   if (is_player)                // function can also be called by EL_PENGUIN
13767   {
13768     if (player->MovPos == 0)
13769     {
13770       player->is_digging = FALSE;
13771       player->is_collecting = FALSE;
13772     }
13773
13774     if (player->MovPos == 0)    // last pushing move finished
13775       player->is_pushing = FALSE;
13776
13777     if (mode == DF_NO_PUSH)     // player just stopped pushing
13778     {
13779       player->is_switching = FALSE;
13780       player->push_delay = -1;
13781
13782       return MP_NO_ACTION;
13783     }
13784   }
13785
13786   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13787     old_element = Back[jx][jy];
13788
13789   // in case of element dropped at player position, check background
13790   else if (Back[jx][jy] != EL_EMPTY &&
13791            game.engine_version >= VERSION_IDENT(2,2,0,0))
13792     old_element = Back[jx][jy];
13793
13794   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13795     return MP_NO_ACTION;        // field has no opening in this direction
13796
13797   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13798     return MP_NO_ACTION;        // field has no opening in this direction
13799
13800   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13801   {
13802     SplashAcid(x, y);
13803
13804     Tile[jx][jy] = player->artwork_element;
13805     InitMovingField(jx, jy, MV_DOWN);
13806     Store[jx][jy] = EL_ACID;
13807     ContinueMoving(jx, jy);
13808     BuryPlayer(player);
13809
13810     return MP_DONT_RUN_INTO;
13811   }
13812
13813   if (player_can_move && DONT_RUN_INTO(element))
13814   {
13815     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13816
13817     return MP_DONT_RUN_INTO;
13818   }
13819
13820   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13821     return MP_NO_ACTION;
13822
13823   collect_count = element_info[element].collect_count_initial;
13824
13825   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
13826     return MP_NO_ACTION;
13827
13828   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13829     player_can_move = player_can_move_or_snap;
13830
13831   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13832       game.engine_version >= VERSION_IDENT(2,2,0,0))
13833   {
13834     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13835                                player->index_bit, dig_side);
13836     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13837                                         player->index_bit, dig_side);
13838
13839     if (element == EL_DC_LANDMINE)
13840       Bang(x, y);
13841
13842     if (Tile[x][y] != element)          // field changed by snapping
13843       return MP_ACTION;
13844
13845     return MP_NO_ACTION;
13846   }
13847
13848   if (player->gravity && is_player && !player->is_auto_moving &&
13849       canFallDown(player) && move_direction != MV_DOWN &&
13850       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13851     return MP_NO_ACTION;        // player cannot walk here due to gravity
13852
13853   if (player_can_move &&
13854       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13855   {
13856     int sound_element = SND_ELEMENT(element);
13857     int sound_action = ACTION_WALKING;
13858
13859     if (IS_RND_GATE(element))
13860     {
13861       if (!player->key[RND_GATE_NR(element)])
13862         return MP_NO_ACTION;
13863     }
13864     else if (IS_RND_GATE_GRAY(element))
13865     {
13866       if (!player->key[RND_GATE_GRAY_NR(element)])
13867         return MP_NO_ACTION;
13868     }
13869     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13870     {
13871       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13872         return MP_NO_ACTION;
13873     }
13874     else if (element == EL_EXIT_OPEN ||
13875              element == EL_EM_EXIT_OPEN ||
13876              element == EL_EM_EXIT_OPENING ||
13877              element == EL_STEEL_EXIT_OPEN ||
13878              element == EL_EM_STEEL_EXIT_OPEN ||
13879              element == EL_EM_STEEL_EXIT_OPENING ||
13880              element == EL_SP_EXIT_OPEN ||
13881              element == EL_SP_EXIT_OPENING)
13882     {
13883       sound_action = ACTION_PASSING;    // player is passing exit
13884     }
13885     else if (element == EL_EMPTY)
13886     {
13887       sound_action = ACTION_MOVING;             // nothing to walk on
13888     }
13889
13890     // play sound from background or player, whatever is available
13891     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13892       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13893     else
13894       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13895   }
13896   else if (player_can_move &&
13897            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13898   {
13899     if (!ACCESS_FROM(element, opposite_direction))
13900       return MP_NO_ACTION;      // field not accessible from this direction
13901
13902     if (CAN_MOVE(element))      // only fixed elements can be passed!
13903       return MP_NO_ACTION;
13904
13905     if (IS_EM_GATE(element))
13906     {
13907       if (!player->key[EM_GATE_NR(element)])
13908         return MP_NO_ACTION;
13909     }
13910     else if (IS_EM_GATE_GRAY(element))
13911     {
13912       if (!player->key[EM_GATE_GRAY_NR(element)])
13913         return MP_NO_ACTION;
13914     }
13915     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13916     {
13917       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13918         return MP_NO_ACTION;
13919     }
13920     else if (IS_EMC_GATE(element))
13921     {
13922       if (!player->key[EMC_GATE_NR(element)])
13923         return MP_NO_ACTION;
13924     }
13925     else if (IS_EMC_GATE_GRAY(element))
13926     {
13927       if (!player->key[EMC_GATE_GRAY_NR(element)])
13928         return MP_NO_ACTION;
13929     }
13930     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13931     {
13932       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13933         return MP_NO_ACTION;
13934     }
13935     else if (element == EL_DC_GATE_WHITE ||
13936              element == EL_DC_GATE_WHITE_GRAY ||
13937              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13938     {
13939       if (player->num_white_keys == 0)
13940         return MP_NO_ACTION;
13941
13942       player->num_white_keys--;
13943     }
13944     else if (IS_SP_PORT(element))
13945     {
13946       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13947           element == EL_SP_GRAVITY_PORT_RIGHT ||
13948           element == EL_SP_GRAVITY_PORT_UP ||
13949           element == EL_SP_GRAVITY_PORT_DOWN)
13950         player->gravity = !player->gravity;
13951       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13952                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13953                element == EL_SP_GRAVITY_ON_PORT_UP ||
13954                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13955         player->gravity = TRUE;
13956       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13957                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13958                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13959                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13960         player->gravity = FALSE;
13961     }
13962
13963     // automatically move to the next field with double speed
13964     player->programmed_action = move_direction;
13965
13966     if (player->move_delay_reset_counter == 0)
13967     {
13968       player->move_delay_reset_counter = 2;     // two double speed steps
13969
13970       DOUBLE_PLAYER_SPEED(player);
13971     }
13972
13973     PlayLevelSoundAction(x, y, ACTION_PASSING);
13974   }
13975   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13976   {
13977     RemoveField(x, y);
13978
13979     if (mode != DF_SNAP)
13980     {
13981       GfxElement[x][y] = GFX_ELEMENT(element);
13982       player->is_digging = TRUE;
13983     }
13984
13985     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13986
13987     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13988                                         player->index_bit, dig_side);
13989
13990     if (mode == DF_SNAP)
13991     {
13992       if (level.block_snap_field)
13993         setFieldForSnapping(x, y, element, move_direction);
13994       else
13995         TestIfElementTouchesCustomElement(x, y);        // for empty space
13996
13997       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13998                                           player->index_bit, dig_side);
13999     }
14000   }
14001   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14002   {
14003     RemoveField(x, y);
14004
14005     if (is_player && mode != DF_SNAP)
14006     {
14007       GfxElement[x][y] = element;
14008       player->is_collecting = TRUE;
14009     }
14010
14011     if (element == EL_SPEED_PILL)
14012     {
14013       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14014     }
14015     else if (element == EL_EXTRA_TIME && level.time > 0)
14016     {
14017       TimeLeft += level.extra_time;
14018
14019       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14020
14021       DisplayGameControlValues();
14022     }
14023     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14024     {
14025       player->shield_normal_time_left += level.shield_normal_time;
14026       if (element == EL_SHIELD_DEADLY)
14027         player->shield_deadly_time_left += level.shield_deadly_time;
14028     }
14029     else if (element == EL_DYNAMITE ||
14030              element == EL_EM_DYNAMITE ||
14031              element == EL_SP_DISK_RED)
14032     {
14033       if (player->inventory_size < MAX_INVENTORY_SIZE)
14034         player->inventory_element[player->inventory_size++] = element;
14035
14036       DrawGameDoorValues();
14037     }
14038     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14039     {
14040       player->dynabomb_count++;
14041       player->dynabombs_left++;
14042     }
14043     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14044     {
14045       player->dynabomb_size++;
14046     }
14047     else if (element == EL_DYNABOMB_INCREASE_POWER)
14048     {
14049       player->dynabomb_xl = TRUE;
14050     }
14051     else if (IS_KEY(element))
14052     {
14053       player->key[KEY_NR(element)] = TRUE;
14054
14055       DrawGameDoorValues();
14056     }
14057     else if (element == EL_DC_KEY_WHITE)
14058     {
14059       player->num_white_keys++;
14060
14061       // display white keys?
14062       // DrawGameDoorValues();
14063     }
14064     else if (IS_ENVELOPE(element))
14065     {
14066       player->show_envelope = element;
14067     }
14068     else if (element == EL_EMC_LENSES)
14069     {
14070       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14071
14072       RedrawAllInvisibleElementsForLenses();
14073     }
14074     else if (element == EL_EMC_MAGNIFIER)
14075     {
14076       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14077
14078       RedrawAllInvisibleElementsForMagnifier();
14079     }
14080     else if (IS_DROPPABLE(element) ||
14081              IS_THROWABLE(element))     // can be collected and dropped
14082     {
14083       int i;
14084
14085       if (collect_count == 0)
14086         player->inventory_infinite_element = element;
14087       else
14088         for (i = 0; i < collect_count; i++)
14089           if (player->inventory_size < MAX_INVENTORY_SIZE)
14090             player->inventory_element[player->inventory_size++] = element;
14091
14092       DrawGameDoorValues();
14093     }
14094     else if (collect_count > 0)
14095     {
14096       game.gems_still_needed -= collect_count;
14097       if (game.gems_still_needed < 0)
14098         game.gems_still_needed = 0;
14099
14100       game.snapshot.collected_item = TRUE;
14101
14102       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14103
14104       DisplayGameControlValues();
14105     }
14106
14107     RaiseScoreElement(element);
14108     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14109
14110     if (is_player)
14111       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14112                                           player->index_bit, dig_side);
14113
14114     if (mode == DF_SNAP)
14115     {
14116       if (level.block_snap_field)
14117         setFieldForSnapping(x, y, element, move_direction);
14118       else
14119         TestIfElementTouchesCustomElement(x, y);        // for empty space
14120
14121       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14122                                           player->index_bit, dig_side);
14123     }
14124   }
14125   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14126   {
14127     if (mode == DF_SNAP && element != EL_BD_ROCK)
14128       return MP_NO_ACTION;
14129
14130     if (CAN_FALL(element) && dy)
14131       return MP_NO_ACTION;
14132
14133     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14134         !(element == EL_SPRING && level.use_spring_bug))
14135       return MP_NO_ACTION;
14136
14137     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14138         ((move_direction & MV_VERTICAL &&
14139           ((element_info[element].move_pattern & MV_LEFT &&
14140             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14141            (element_info[element].move_pattern & MV_RIGHT &&
14142             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14143          (move_direction & MV_HORIZONTAL &&
14144           ((element_info[element].move_pattern & MV_UP &&
14145             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14146            (element_info[element].move_pattern & MV_DOWN &&
14147             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14148       return MP_NO_ACTION;
14149
14150     // do not push elements already moving away faster than player
14151     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14152         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14153       return MP_NO_ACTION;
14154
14155     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14156     {
14157       if (player->push_delay_value == -1 || !player_was_pushing)
14158         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14159     }
14160     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14161     {
14162       if (player->push_delay_value == -1)
14163         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14164     }
14165     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14166     {
14167       if (!player->is_pushing)
14168         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14169     }
14170
14171     player->is_pushing = TRUE;
14172     player->is_active = TRUE;
14173
14174     if (!(IN_LEV_FIELD(nextx, nexty) &&
14175           (IS_FREE(nextx, nexty) ||
14176            (IS_SB_ELEMENT(element) &&
14177             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14178            (IS_CUSTOM_ELEMENT(element) &&
14179             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14180       return MP_NO_ACTION;
14181
14182     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14183       return MP_NO_ACTION;
14184
14185     if (player->push_delay == -1)       // new pushing; restart delay
14186       player->push_delay = 0;
14187
14188     if (player->push_delay < player->push_delay_value &&
14189         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14190         element != EL_SPRING && element != EL_BALLOON)
14191     {
14192       // make sure that there is no move delay before next try to push
14193       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14194         player->move_delay = 0;
14195
14196       return MP_NO_ACTION;
14197     }
14198
14199     if (IS_CUSTOM_ELEMENT(element) &&
14200         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14201     {
14202       if (!DigFieldByCE(nextx, nexty, element))
14203         return MP_NO_ACTION;
14204     }
14205
14206     if (IS_SB_ELEMENT(element))
14207     {
14208       boolean sokoban_task_solved = FALSE;
14209
14210       if (element == EL_SOKOBAN_FIELD_FULL)
14211       {
14212         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14213
14214         IncrementSokobanFieldsNeeded();
14215         IncrementSokobanObjectsNeeded();
14216       }
14217
14218       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14219       {
14220         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14221
14222         DecrementSokobanFieldsNeeded();
14223         DecrementSokobanObjectsNeeded();
14224
14225         // sokoban object was pushed from empty field to sokoban field
14226         if (Back[x][y] == EL_EMPTY)
14227           sokoban_task_solved = TRUE;
14228       }
14229
14230       Tile[x][y] = EL_SOKOBAN_OBJECT;
14231
14232       if (Back[x][y] == Back[nextx][nexty])
14233         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14234       else if (Back[x][y] != 0)
14235         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14236                                     ACTION_EMPTYING);
14237       else
14238         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14239                                     ACTION_FILLING);
14240
14241       if (sokoban_task_solved &&
14242           game.sokoban_fields_still_needed == 0 &&
14243           game.sokoban_objects_still_needed == 0 &&
14244           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14245       {
14246         game.players_still_needed = 0;
14247
14248         LevelSolved();
14249
14250         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14251       }
14252     }
14253     else
14254       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14255
14256     InitMovingField(x, y, move_direction);
14257     GfxAction[x][y] = ACTION_PUSHING;
14258
14259     if (mode == DF_SNAP)
14260       ContinueMoving(x, y);
14261     else
14262       MovPos[x][y] = (dx != 0 ? dx : dy);
14263
14264     Pushed[x][y] = TRUE;
14265     Pushed[nextx][nexty] = TRUE;
14266
14267     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14268       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14269     else
14270       player->push_delay_value = -1;    // get new value later
14271
14272     // check for element change _after_ element has been pushed
14273     if (game.use_change_when_pushing_bug)
14274     {
14275       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14276                                  player->index_bit, dig_side);
14277       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14278                                           player->index_bit, dig_side);
14279     }
14280   }
14281   else if (IS_SWITCHABLE(element))
14282   {
14283     if (PLAYER_SWITCHING(player, x, y))
14284     {
14285       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14286                                           player->index_bit, dig_side);
14287
14288       return MP_ACTION;
14289     }
14290
14291     player->is_switching = TRUE;
14292     player->switch_x = x;
14293     player->switch_y = y;
14294
14295     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14296
14297     if (element == EL_ROBOT_WHEEL)
14298     {
14299       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14300
14301       game.robot_wheel_x = x;
14302       game.robot_wheel_y = y;
14303       game.robot_wheel_active = TRUE;
14304
14305       TEST_DrawLevelField(x, y);
14306     }
14307     else if (element == EL_SP_TERMINAL)
14308     {
14309       int xx, yy;
14310
14311       SCAN_PLAYFIELD(xx, yy)
14312       {
14313         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14314         {
14315           Bang(xx, yy);
14316         }
14317         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14318         {
14319           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14320
14321           ResetGfxAnimation(xx, yy);
14322           TEST_DrawLevelField(xx, yy);
14323         }
14324       }
14325     }
14326     else if (IS_BELT_SWITCH(element))
14327     {
14328       ToggleBeltSwitch(x, y);
14329     }
14330     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14331              element == EL_SWITCHGATE_SWITCH_DOWN ||
14332              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14333              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14334     {
14335       ToggleSwitchgateSwitch(x, y);
14336     }
14337     else if (element == EL_LIGHT_SWITCH ||
14338              element == EL_LIGHT_SWITCH_ACTIVE)
14339     {
14340       ToggleLightSwitch(x, y);
14341     }
14342     else if (element == EL_TIMEGATE_SWITCH ||
14343              element == EL_DC_TIMEGATE_SWITCH)
14344     {
14345       ActivateTimegateSwitch(x, y);
14346     }
14347     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14348              element == EL_BALLOON_SWITCH_RIGHT ||
14349              element == EL_BALLOON_SWITCH_UP    ||
14350              element == EL_BALLOON_SWITCH_DOWN  ||
14351              element == EL_BALLOON_SWITCH_NONE  ||
14352              element == EL_BALLOON_SWITCH_ANY)
14353     {
14354       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14355                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14356                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14357                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14358                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14359                              move_direction);
14360     }
14361     else if (element == EL_LAMP)
14362     {
14363       Tile[x][y] = EL_LAMP_ACTIVE;
14364       game.lights_still_needed--;
14365
14366       ResetGfxAnimation(x, y);
14367       TEST_DrawLevelField(x, y);
14368     }
14369     else if (element == EL_TIME_ORB_FULL)
14370     {
14371       Tile[x][y] = EL_TIME_ORB_EMPTY;
14372
14373       if (level.time > 0 || level.use_time_orb_bug)
14374       {
14375         TimeLeft += level.time_orb_time;
14376         game.no_time_limit = FALSE;
14377
14378         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14379
14380         DisplayGameControlValues();
14381       }
14382
14383       ResetGfxAnimation(x, y);
14384       TEST_DrawLevelField(x, y);
14385     }
14386     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14387              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14388     {
14389       int xx, yy;
14390
14391       game.ball_active = !game.ball_active;
14392
14393       SCAN_PLAYFIELD(xx, yy)
14394       {
14395         int e = Tile[xx][yy];
14396
14397         if (game.ball_active)
14398         {
14399           if (e == EL_EMC_MAGIC_BALL)
14400             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14401           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14402             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14403         }
14404         else
14405         {
14406           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14407             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14408           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14409             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14410         }
14411       }
14412     }
14413
14414     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14415                                         player->index_bit, dig_side);
14416
14417     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14418                                         player->index_bit, dig_side);
14419
14420     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14421                                         player->index_bit, dig_side);
14422
14423     return MP_ACTION;
14424   }
14425   else
14426   {
14427     if (!PLAYER_SWITCHING(player, x, y))
14428     {
14429       player->is_switching = TRUE;
14430       player->switch_x = x;
14431       player->switch_y = y;
14432
14433       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14434                                  player->index_bit, dig_side);
14435       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14436                                           player->index_bit, dig_side);
14437
14438       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14439                                  player->index_bit, dig_side);
14440       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14441                                           player->index_bit, dig_side);
14442     }
14443
14444     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14445                                player->index_bit, dig_side);
14446     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14447                                         player->index_bit, dig_side);
14448
14449     return MP_NO_ACTION;
14450   }
14451
14452   player->push_delay = -1;
14453
14454   if (is_player)                // function can also be called by EL_PENGUIN
14455   {
14456     if (Tile[x][y] != element)          // really digged/collected something
14457     {
14458       player->is_collecting = !player->is_digging;
14459       player->is_active = TRUE;
14460     }
14461   }
14462
14463   return MP_MOVING;
14464 }
14465
14466 static boolean DigFieldByCE(int x, int y, int digging_element)
14467 {
14468   int element = Tile[x][y];
14469
14470   if (!IS_FREE(x, y))
14471   {
14472     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14473                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14474                   ACTION_BREAKING);
14475
14476     // no element can dig solid indestructible elements
14477     if (IS_INDESTRUCTIBLE(element) &&
14478         !IS_DIGGABLE(element) &&
14479         !IS_COLLECTIBLE(element))
14480       return FALSE;
14481
14482     if (AmoebaNr[x][y] &&
14483         (element == EL_AMOEBA_FULL ||
14484          element == EL_BD_AMOEBA ||
14485          element == EL_AMOEBA_GROWING))
14486     {
14487       AmoebaCnt[AmoebaNr[x][y]]--;
14488       AmoebaCnt2[AmoebaNr[x][y]]--;
14489     }
14490
14491     if (IS_MOVING(x, y))
14492       RemoveMovingField(x, y);
14493     else
14494     {
14495       RemoveField(x, y);
14496       TEST_DrawLevelField(x, y);
14497     }
14498
14499     // if digged element was about to explode, prevent the explosion
14500     ExplodeField[x][y] = EX_TYPE_NONE;
14501
14502     PlayLevelSoundAction(x, y, action);
14503   }
14504
14505   Store[x][y] = EL_EMPTY;
14506
14507   // this makes it possible to leave the removed element again
14508   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14509     Store[x][y] = element;
14510
14511   return TRUE;
14512 }
14513
14514 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14515 {
14516   int jx = player->jx, jy = player->jy;
14517   int x = jx + dx, y = jy + dy;
14518   int snap_direction = (dx == -1 ? MV_LEFT  :
14519                         dx == +1 ? MV_RIGHT :
14520                         dy == -1 ? MV_UP    :
14521                         dy == +1 ? MV_DOWN  : MV_NONE);
14522   boolean can_continue_snapping = (level.continuous_snapping &&
14523                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14524
14525   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14526     return FALSE;
14527
14528   if (!player->active || !IN_LEV_FIELD(x, y))
14529     return FALSE;
14530
14531   if (dx && dy)
14532     return FALSE;
14533
14534   if (!dx && !dy)
14535   {
14536     if (player->MovPos == 0)
14537       player->is_pushing = FALSE;
14538
14539     player->is_snapping = FALSE;
14540
14541     if (player->MovPos == 0)
14542     {
14543       player->is_moving = FALSE;
14544       player->is_digging = FALSE;
14545       player->is_collecting = FALSE;
14546     }
14547
14548     return FALSE;
14549   }
14550
14551   // prevent snapping with already pressed snap key when not allowed
14552   if (player->is_snapping && !can_continue_snapping)
14553     return FALSE;
14554
14555   player->MovDir = snap_direction;
14556
14557   if (player->MovPos == 0)
14558   {
14559     player->is_moving = FALSE;
14560     player->is_digging = FALSE;
14561     player->is_collecting = FALSE;
14562   }
14563
14564   player->is_dropping = FALSE;
14565   player->is_dropping_pressed = FALSE;
14566   player->drop_pressed_delay = 0;
14567
14568   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14569     return FALSE;
14570
14571   player->is_snapping = TRUE;
14572   player->is_active = TRUE;
14573
14574   if (player->MovPos == 0)
14575   {
14576     player->is_moving = FALSE;
14577     player->is_digging = FALSE;
14578     player->is_collecting = FALSE;
14579   }
14580
14581   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14582     TEST_DrawLevelField(player->last_jx, player->last_jy);
14583
14584   TEST_DrawLevelField(x, y);
14585
14586   return TRUE;
14587 }
14588
14589 static boolean DropElement(struct PlayerInfo *player)
14590 {
14591   int old_element, new_element;
14592   int dropx = player->jx, dropy = player->jy;
14593   int drop_direction = player->MovDir;
14594   int drop_side = drop_direction;
14595   int drop_element = get_next_dropped_element(player);
14596
14597   /* do not drop an element on top of another element; when holding drop key
14598      pressed without moving, dropped element must move away before the next
14599      element can be dropped (this is especially important if the next element
14600      is dynamite, which can be placed on background for historical reasons) */
14601   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
14602     return MP_ACTION;
14603
14604   if (IS_THROWABLE(drop_element))
14605   {
14606     dropx += GET_DX_FROM_DIR(drop_direction);
14607     dropy += GET_DY_FROM_DIR(drop_direction);
14608
14609     if (!IN_LEV_FIELD(dropx, dropy))
14610       return FALSE;
14611   }
14612
14613   old_element = Tile[dropx][dropy];     // old element at dropping position
14614   new_element = drop_element;           // default: no change when dropping
14615
14616   // check if player is active, not moving and ready to drop
14617   if (!player->active || player->MovPos || player->drop_delay > 0)
14618     return FALSE;
14619
14620   // check if player has anything that can be dropped
14621   if (new_element == EL_UNDEFINED)
14622     return FALSE;
14623
14624   // only set if player has anything that can be dropped
14625   player->is_dropping_pressed = TRUE;
14626
14627   // check if drop key was pressed long enough for EM style dynamite
14628   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14629     return FALSE;
14630
14631   // check if anything can be dropped at the current position
14632   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14633     return FALSE;
14634
14635   // collected custom elements can only be dropped on empty fields
14636   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14637     return FALSE;
14638
14639   if (old_element != EL_EMPTY)
14640     Back[dropx][dropy] = old_element;   // store old element on this field
14641
14642   ResetGfxAnimation(dropx, dropy);
14643   ResetRandomAnimationValue(dropx, dropy);
14644
14645   if (player->inventory_size > 0 ||
14646       player->inventory_infinite_element != EL_UNDEFINED)
14647   {
14648     if (player->inventory_size > 0)
14649     {
14650       player->inventory_size--;
14651
14652       DrawGameDoorValues();
14653
14654       if (new_element == EL_DYNAMITE)
14655         new_element = EL_DYNAMITE_ACTIVE;
14656       else if (new_element == EL_EM_DYNAMITE)
14657         new_element = EL_EM_DYNAMITE_ACTIVE;
14658       else if (new_element == EL_SP_DISK_RED)
14659         new_element = EL_SP_DISK_RED_ACTIVE;
14660     }
14661
14662     Tile[dropx][dropy] = new_element;
14663
14664     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14665       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14666                           el2img(Tile[dropx][dropy]), 0);
14667
14668     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14669
14670     // needed if previous element just changed to "empty" in the last frame
14671     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14672
14673     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14674                                player->index_bit, drop_side);
14675     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14676                                         CE_PLAYER_DROPS_X,
14677                                         player->index_bit, drop_side);
14678
14679     TestIfElementTouchesCustomElement(dropx, dropy);
14680   }
14681   else          // player is dropping a dyna bomb
14682   {
14683     player->dynabombs_left--;
14684
14685     Tile[dropx][dropy] = new_element;
14686
14687     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14688       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14689                           el2img(Tile[dropx][dropy]), 0);
14690
14691     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14692   }
14693
14694   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
14695     InitField_WithBug1(dropx, dropy, FALSE);
14696
14697   new_element = Tile[dropx][dropy];     // element might have changed
14698
14699   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14700       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14701   {
14702     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14703       MovDir[dropx][dropy] = drop_direction;
14704
14705     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14706
14707     // do not cause impact style collision by dropping elements that can fall
14708     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14709   }
14710
14711   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14712   player->is_dropping = TRUE;
14713
14714   player->drop_pressed_delay = 0;
14715   player->is_dropping_pressed = FALSE;
14716
14717   player->drop_x = dropx;
14718   player->drop_y = dropy;
14719
14720   return TRUE;
14721 }
14722
14723 // ----------------------------------------------------------------------------
14724 // game sound playing functions
14725 // ----------------------------------------------------------------------------
14726
14727 static int *loop_sound_frame = NULL;
14728 static int *loop_sound_volume = NULL;
14729
14730 void InitPlayLevelSound(void)
14731 {
14732   int num_sounds = getSoundListSize();
14733
14734   checked_free(loop_sound_frame);
14735   checked_free(loop_sound_volume);
14736
14737   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14738   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14739 }
14740
14741 static void PlayLevelSound(int x, int y, int nr)
14742 {
14743   int sx = SCREENX(x), sy = SCREENY(y);
14744   int volume, stereo_position;
14745   int max_distance = 8;
14746   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14747
14748   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14749       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14750     return;
14751
14752   if (!IN_LEV_FIELD(x, y) ||
14753       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14754       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14755     return;
14756
14757   volume = SOUND_MAX_VOLUME;
14758
14759   if (!IN_SCR_FIELD(sx, sy))
14760   {
14761     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14762     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14763
14764     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14765   }
14766
14767   stereo_position = (SOUND_MAX_LEFT +
14768                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14769                      (SCR_FIELDX + 2 * max_distance));
14770
14771   if (IS_LOOP_SOUND(nr))
14772   {
14773     /* This assures that quieter loop sounds do not overwrite louder ones,
14774        while restarting sound volume comparison with each new game frame. */
14775
14776     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14777       return;
14778
14779     loop_sound_volume[nr] = volume;
14780     loop_sound_frame[nr] = FrameCounter;
14781   }
14782
14783   PlaySoundExt(nr, volume, stereo_position, type);
14784 }
14785
14786 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14787 {
14788   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14789                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14790                  y < LEVELY(BY1) ? LEVELY(BY1) :
14791                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14792                  sound_action);
14793 }
14794
14795 static void PlayLevelSoundAction(int x, int y, int action)
14796 {
14797   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
14798 }
14799
14800 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14801 {
14802   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14803
14804   if (sound_effect != SND_UNDEFINED)
14805     PlayLevelSound(x, y, sound_effect);
14806 }
14807
14808 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14809                                               int action)
14810 {
14811   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14812
14813   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14814     PlayLevelSound(x, y, sound_effect);
14815 }
14816
14817 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14818 {
14819   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
14820
14821   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14822     PlayLevelSound(x, y, sound_effect);
14823 }
14824
14825 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14826 {
14827   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
14828
14829   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14830     StopSound(sound_effect);
14831 }
14832
14833 static int getLevelMusicNr(void)
14834 {
14835   if (levelset.music[level_nr] != MUS_UNDEFINED)
14836     return levelset.music[level_nr];            // from config file
14837   else
14838     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
14839 }
14840
14841 static void FadeLevelSounds(void)
14842 {
14843   FadeSounds();
14844 }
14845
14846 static void FadeLevelMusic(void)
14847 {
14848   int music_nr = getLevelMusicNr();
14849   char *curr_music = getCurrentlyPlayingMusicFilename();
14850   char *next_music = getMusicInfoEntryFilename(music_nr);
14851
14852   if (!strEqual(curr_music, next_music))
14853     FadeMusic();
14854 }
14855
14856 void FadeLevelSoundsAndMusic(void)
14857 {
14858   FadeLevelSounds();
14859   FadeLevelMusic();
14860 }
14861
14862 static void PlayLevelMusic(void)
14863 {
14864   int music_nr = getLevelMusicNr();
14865   char *curr_music = getCurrentlyPlayingMusicFilename();
14866   char *next_music = getMusicInfoEntryFilename(music_nr);
14867
14868   if (!strEqual(curr_music, next_music))
14869     PlayMusicLoop(music_nr);
14870 }
14871
14872 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14873 {
14874   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
14875   int offset = 0;
14876   int x = xx - offset;
14877   int y = yy - offset;
14878
14879   switch (sample)
14880   {
14881     case SOUND_blank:
14882       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14883       break;
14884
14885     case SOUND_roll:
14886       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14887       break;
14888
14889     case SOUND_stone:
14890       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14891       break;
14892
14893     case SOUND_nut:
14894       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14895       break;
14896
14897     case SOUND_crack:
14898       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14899       break;
14900
14901     case SOUND_bug:
14902       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14903       break;
14904
14905     case SOUND_tank:
14906       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14907       break;
14908
14909     case SOUND_android_clone:
14910       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14911       break;
14912
14913     case SOUND_android_move:
14914       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14915       break;
14916
14917     case SOUND_spring:
14918       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14919       break;
14920
14921     case SOUND_slurp:
14922       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14923       break;
14924
14925     case SOUND_eater:
14926       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14927       break;
14928
14929     case SOUND_eater_eat:
14930       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14931       break;
14932
14933     case SOUND_alien:
14934       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14935       break;
14936
14937     case SOUND_collect:
14938       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14939       break;
14940
14941     case SOUND_diamond:
14942       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14943       break;
14944
14945     case SOUND_squash:
14946       // !!! CHECK THIS !!!
14947 #if 1
14948       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14949 #else
14950       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14951 #endif
14952       break;
14953
14954     case SOUND_wonderfall:
14955       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14956       break;
14957
14958     case SOUND_drip:
14959       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14960       break;
14961
14962     case SOUND_push:
14963       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14964       break;
14965
14966     case SOUND_dirt:
14967       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14968       break;
14969
14970     case SOUND_acid:
14971       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14972       break;
14973
14974     case SOUND_ball:
14975       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14976       break;
14977
14978     case SOUND_slide:
14979       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14980       break;
14981
14982     case SOUND_wonder:
14983       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14984       break;
14985
14986     case SOUND_door:
14987       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14988       break;
14989
14990     case SOUND_exit_open:
14991       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14992       break;
14993
14994     case SOUND_exit_leave:
14995       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14996       break;
14997
14998     case SOUND_dynamite:
14999       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15000       break;
15001
15002     case SOUND_tick:
15003       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15004       break;
15005
15006     case SOUND_press:
15007       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15008       break;
15009
15010     case SOUND_wheel:
15011       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15012       break;
15013
15014     case SOUND_boom:
15015       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15016       break;
15017
15018     case SOUND_die:
15019       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15020       break;
15021
15022     case SOUND_time:
15023       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15024       break;
15025
15026     default:
15027       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15028       break;
15029   }
15030 }
15031
15032 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15033 {
15034   int element = map_element_SP_to_RND(element_sp);
15035   int action = map_action_SP_to_RND(action_sp);
15036   int offset = (setup.sp_show_border_elements ? 0 : 1);
15037   int x = xx - offset;
15038   int y = yy - offset;
15039
15040   PlayLevelSoundElementAction(x, y, element, action);
15041 }
15042
15043 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15044 {
15045   int element = map_element_MM_to_RND(element_mm);
15046   int action = map_action_MM_to_RND(action_mm);
15047   int offset = 0;
15048   int x = xx - offset;
15049   int y = yy - offset;
15050
15051   if (!IS_MM_ELEMENT(element))
15052     element = EL_MM_DEFAULT;
15053
15054   PlayLevelSoundElementAction(x, y, element, action);
15055 }
15056
15057 void PlaySound_MM(int sound_mm)
15058 {
15059   int sound = map_sound_MM_to_RND(sound_mm);
15060
15061   if (sound == SND_UNDEFINED)
15062     return;
15063
15064   PlaySound(sound);
15065 }
15066
15067 void PlaySoundLoop_MM(int sound_mm)
15068 {
15069   int sound = map_sound_MM_to_RND(sound_mm);
15070
15071   if (sound == SND_UNDEFINED)
15072     return;
15073
15074   PlaySoundLoop(sound);
15075 }
15076
15077 void StopSound_MM(int sound_mm)
15078 {
15079   int sound = map_sound_MM_to_RND(sound_mm);
15080
15081   if (sound == SND_UNDEFINED)
15082     return;
15083
15084   StopSound(sound);
15085 }
15086
15087 void RaiseScore(int value)
15088 {
15089   game.score += value;
15090
15091   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15092
15093   DisplayGameControlValues();
15094 }
15095
15096 void RaiseScoreElement(int element)
15097 {
15098   switch (element)
15099   {
15100     case EL_EMERALD:
15101     case EL_BD_DIAMOND:
15102     case EL_EMERALD_YELLOW:
15103     case EL_EMERALD_RED:
15104     case EL_EMERALD_PURPLE:
15105     case EL_SP_INFOTRON:
15106       RaiseScore(level.score[SC_EMERALD]);
15107       break;
15108     case EL_DIAMOND:
15109       RaiseScore(level.score[SC_DIAMOND]);
15110       break;
15111     case EL_CRYSTAL:
15112       RaiseScore(level.score[SC_CRYSTAL]);
15113       break;
15114     case EL_PEARL:
15115       RaiseScore(level.score[SC_PEARL]);
15116       break;
15117     case EL_BUG:
15118     case EL_BD_BUTTERFLY:
15119     case EL_SP_ELECTRON:
15120       RaiseScore(level.score[SC_BUG]);
15121       break;
15122     case EL_SPACESHIP:
15123     case EL_BD_FIREFLY:
15124     case EL_SP_SNIKSNAK:
15125       RaiseScore(level.score[SC_SPACESHIP]);
15126       break;
15127     case EL_YAMYAM:
15128     case EL_DARK_YAMYAM:
15129       RaiseScore(level.score[SC_YAMYAM]);
15130       break;
15131     case EL_ROBOT:
15132       RaiseScore(level.score[SC_ROBOT]);
15133       break;
15134     case EL_PACMAN:
15135       RaiseScore(level.score[SC_PACMAN]);
15136       break;
15137     case EL_NUT:
15138       RaiseScore(level.score[SC_NUT]);
15139       break;
15140     case EL_DYNAMITE:
15141     case EL_EM_DYNAMITE:
15142     case EL_SP_DISK_RED:
15143     case EL_DYNABOMB_INCREASE_NUMBER:
15144     case EL_DYNABOMB_INCREASE_SIZE:
15145     case EL_DYNABOMB_INCREASE_POWER:
15146       RaiseScore(level.score[SC_DYNAMITE]);
15147       break;
15148     case EL_SHIELD_NORMAL:
15149     case EL_SHIELD_DEADLY:
15150       RaiseScore(level.score[SC_SHIELD]);
15151       break;
15152     case EL_EXTRA_TIME:
15153       RaiseScore(level.extra_time_score);
15154       break;
15155     case EL_KEY_1:
15156     case EL_KEY_2:
15157     case EL_KEY_3:
15158     case EL_KEY_4:
15159     case EL_EM_KEY_1:
15160     case EL_EM_KEY_2:
15161     case EL_EM_KEY_3:
15162     case EL_EM_KEY_4:
15163     case EL_EMC_KEY_5:
15164     case EL_EMC_KEY_6:
15165     case EL_EMC_KEY_7:
15166     case EL_EMC_KEY_8:
15167     case EL_DC_KEY_WHITE:
15168       RaiseScore(level.score[SC_KEY]);
15169       break;
15170     default:
15171       RaiseScore(element_info[element].collect_score);
15172       break;
15173   }
15174 }
15175
15176 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15177 {
15178   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15179   {
15180     // closing door required in case of envelope style request dialogs
15181     if (!skip_request)
15182     {
15183       // prevent short reactivation of overlay buttons while closing door
15184       SetOverlayActive(FALSE);
15185
15186       CloseDoor(DOOR_CLOSE_1);
15187     }
15188
15189     if (network.enabled)
15190       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15191     else
15192     {
15193       if (quick_quit)
15194         FadeSkipNextFadeIn();
15195
15196       SetGameStatus(GAME_MODE_MAIN);
15197
15198       DrawMainMenu();
15199     }
15200   }
15201   else          // continue playing the game
15202   {
15203     if (tape.playing && tape.deactivate_display)
15204       TapeDeactivateDisplayOff(TRUE);
15205
15206     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15207
15208     if (tape.playing && tape.deactivate_display)
15209       TapeDeactivateDisplayOn();
15210   }
15211 }
15212
15213 void RequestQuitGame(boolean ask_if_really_quit)
15214 {
15215   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15216   boolean skip_request = game.all_players_gone || quick_quit;
15217
15218   RequestQuitGameExt(skip_request, quick_quit,
15219                      "Do you really want to quit the game?");
15220 }
15221
15222 void RequestRestartGame(char *message)
15223 {
15224   game.restart_game_message = NULL;
15225
15226   boolean has_started_game = hasStartedNetworkGame();
15227   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15228
15229   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15230   {
15231     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15232   }
15233   else
15234   {
15235     SetGameStatus(GAME_MODE_MAIN);
15236
15237     DrawMainMenu();
15238   }
15239 }
15240
15241 void CheckGameOver(void)
15242 {
15243   static boolean last_game_over = FALSE;
15244   static int game_over_delay = 0;
15245   int game_over_delay_value = 50;
15246   boolean game_over = checkGameFailed();
15247
15248   // do not handle game over if request dialog is already active
15249   if (game.request_active)
15250     return;
15251
15252   // do not ask to play again if game was never actually played
15253   if (!game.GamePlayed)
15254     return;
15255
15256   if (!game_over)
15257   {
15258     last_game_over = FALSE;
15259     game_over_delay = game_over_delay_value;
15260
15261     return;
15262   }
15263
15264   if (game_over_delay > 0)
15265   {
15266     game_over_delay--;
15267
15268     return;
15269   }
15270
15271   if (last_game_over != game_over)
15272     game.restart_game_message = (hasStartedNetworkGame() ?
15273                                  "Game over! Play it again?" :
15274                                  "Game over!");
15275
15276   last_game_over = game_over;
15277 }
15278
15279 boolean checkGameSolved(void)
15280 {
15281   // set for all game engines if level was solved
15282   return game.LevelSolved_GameEnd;
15283 }
15284
15285 boolean checkGameFailed(void)
15286 {
15287   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15288     return (game_em.game_over && !game_em.level_solved);
15289   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15290     return (game_sp.game_over && !game_sp.level_solved);
15291   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15292     return (game_mm.game_over && !game_mm.level_solved);
15293   else                          // GAME_ENGINE_TYPE_RND
15294     return (game.GameOver && !game.LevelSolved);
15295 }
15296
15297 boolean checkGameEnded(void)
15298 {
15299   return (checkGameSolved() || checkGameFailed());
15300 }
15301
15302
15303 // ----------------------------------------------------------------------------
15304 // random generator functions
15305 // ----------------------------------------------------------------------------
15306
15307 unsigned int InitEngineRandom_RND(int seed)
15308 {
15309   game.num_random_calls = 0;
15310
15311   return InitEngineRandom(seed);
15312 }
15313
15314 unsigned int RND(int max)
15315 {
15316   if (max > 0)
15317   {
15318     game.num_random_calls++;
15319
15320     return GetEngineRandom(max);
15321   }
15322
15323   return 0;
15324 }
15325
15326
15327 // ----------------------------------------------------------------------------
15328 // game engine snapshot handling functions
15329 // ----------------------------------------------------------------------------
15330
15331 struct EngineSnapshotInfo
15332 {
15333   // runtime values for custom element collect score
15334   int collect_score[NUM_CUSTOM_ELEMENTS];
15335
15336   // runtime values for group element choice position
15337   int choice_pos[NUM_GROUP_ELEMENTS];
15338
15339   // runtime values for belt position animations
15340   int belt_graphic[4][NUM_BELT_PARTS];
15341   int belt_anim_mode[4][NUM_BELT_PARTS];
15342 };
15343
15344 static struct EngineSnapshotInfo engine_snapshot_rnd;
15345 static char *snapshot_level_identifier = NULL;
15346 static int snapshot_level_nr = -1;
15347
15348 static void SaveEngineSnapshotValues_RND(void)
15349 {
15350   static int belt_base_active_element[4] =
15351   {
15352     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15353     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15354     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15355     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15356   };
15357   int i, j;
15358
15359   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15360   {
15361     int element = EL_CUSTOM_START + i;
15362
15363     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15364   }
15365
15366   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15367   {
15368     int element = EL_GROUP_START + i;
15369
15370     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15371   }
15372
15373   for (i = 0; i < 4; i++)
15374   {
15375     for (j = 0; j < NUM_BELT_PARTS; j++)
15376     {
15377       int element = belt_base_active_element[i] + j;
15378       int graphic = el2img(element);
15379       int anim_mode = graphic_info[graphic].anim_mode;
15380
15381       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15382       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15383     }
15384   }
15385 }
15386
15387 static void LoadEngineSnapshotValues_RND(void)
15388 {
15389   unsigned int num_random_calls = game.num_random_calls;
15390   int i, j;
15391
15392   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15393   {
15394     int element = EL_CUSTOM_START + i;
15395
15396     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15397   }
15398
15399   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15400   {
15401     int element = EL_GROUP_START + i;
15402
15403     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15404   }
15405
15406   for (i = 0; i < 4; i++)
15407   {
15408     for (j = 0; j < NUM_BELT_PARTS; j++)
15409     {
15410       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15411       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15412
15413       graphic_info[graphic].anim_mode = anim_mode;
15414     }
15415   }
15416
15417   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15418   {
15419     InitRND(tape.random_seed);
15420     for (i = 0; i < num_random_calls; i++)
15421       RND(1);
15422   }
15423
15424   if (game.num_random_calls != num_random_calls)
15425   {
15426     Error("number of random calls out of sync");
15427     Error("number of random calls should be %d", num_random_calls);
15428     Error("number of random calls is %d", game.num_random_calls);
15429
15430     Fail("this should not happen -- please debug");
15431   }
15432 }
15433
15434 void FreeEngineSnapshotSingle(void)
15435 {
15436   FreeSnapshotSingle();
15437
15438   setString(&snapshot_level_identifier, NULL);
15439   snapshot_level_nr = -1;
15440 }
15441
15442 void FreeEngineSnapshotList(void)
15443 {
15444   FreeSnapshotList();
15445 }
15446
15447 static ListNode *SaveEngineSnapshotBuffers(void)
15448 {
15449   ListNode *buffers = NULL;
15450
15451   // copy some special values to a structure better suited for the snapshot
15452
15453   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15454     SaveEngineSnapshotValues_RND();
15455   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15456     SaveEngineSnapshotValues_EM();
15457   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15458     SaveEngineSnapshotValues_SP(&buffers);
15459   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15460     SaveEngineSnapshotValues_MM(&buffers);
15461
15462   // save values stored in special snapshot structure
15463
15464   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15465     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15466   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15467     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15468   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15469     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15470   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15471     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15472
15473   // save further RND engine values
15474
15475   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15476   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15477   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15478
15479   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15480   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15481   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15482   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15483   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15484
15485   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15486   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15487   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15488
15489   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15490
15491   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15492   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15493
15494   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15495   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15496   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15497   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15498   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15499   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15500   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15501   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15502   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15503   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15504   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15505   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15506   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15507   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15508   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15509   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15510   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15511   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15512
15513   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15514   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15515
15516   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15517   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15518   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15519
15520   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15521   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15522
15523   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15524   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15525   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15526   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15527   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15528
15529   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15530   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15531
15532 #if 0
15533   ListNode *node = engine_snapshot_list_rnd;
15534   int num_bytes = 0;
15535
15536   while (node != NULL)
15537   {
15538     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15539
15540     node = node->next;
15541   }
15542
15543   Debug("game:playing:SaveEngineSnapshotBuffers",
15544         "size of engine snapshot: %d bytes", num_bytes);
15545 #endif
15546
15547   return buffers;
15548 }
15549
15550 void SaveEngineSnapshotSingle(void)
15551 {
15552   ListNode *buffers = SaveEngineSnapshotBuffers();
15553
15554   // finally save all snapshot buffers to single snapshot
15555   SaveSnapshotSingle(buffers);
15556
15557   // save level identification information
15558   setString(&snapshot_level_identifier, leveldir_current->identifier);
15559   snapshot_level_nr = level_nr;
15560 }
15561
15562 boolean CheckSaveEngineSnapshotToList(void)
15563 {
15564   boolean save_snapshot =
15565     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15566      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15567       game.snapshot.changed_action) ||
15568      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15569       game.snapshot.collected_item));
15570
15571   game.snapshot.changed_action = FALSE;
15572   game.snapshot.collected_item = FALSE;
15573   game.snapshot.save_snapshot = save_snapshot;
15574
15575   return save_snapshot;
15576 }
15577
15578 void SaveEngineSnapshotToList(void)
15579 {
15580   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15581       tape.quick_resume)
15582     return;
15583
15584   ListNode *buffers = SaveEngineSnapshotBuffers();
15585
15586   // finally save all snapshot buffers to snapshot list
15587   SaveSnapshotToList(buffers);
15588 }
15589
15590 void SaveEngineSnapshotToListInitial(void)
15591 {
15592   FreeEngineSnapshotList();
15593
15594   SaveEngineSnapshotToList();
15595 }
15596
15597 static void LoadEngineSnapshotValues(void)
15598 {
15599   // restore special values from snapshot structure
15600
15601   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15602     LoadEngineSnapshotValues_RND();
15603   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15604     LoadEngineSnapshotValues_EM();
15605   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15606     LoadEngineSnapshotValues_SP();
15607   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15608     LoadEngineSnapshotValues_MM();
15609 }
15610
15611 void LoadEngineSnapshotSingle(void)
15612 {
15613   LoadSnapshotSingle();
15614
15615   LoadEngineSnapshotValues();
15616 }
15617
15618 static void LoadEngineSnapshot_Undo(int steps)
15619 {
15620   LoadSnapshotFromList_Older(steps);
15621
15622   LoadEngineSnapshotValues();
15623 }
15624
15625 static void LoadEngineSnapshot_Redo(int steps)
15626 {
15627   LoadSnapshotFromList_Newer(steps);
15628
15629   LoadEngineSnapshotValues();
15630 }
15631
15632 boolean CheckEngineSnapshotSingle(void)
15633 {
15634   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15635           snapshot_level_nr == level_nr);
15636 }
15637
15638 boolean CheckEngineSnapshotList(void)
15639 {
15640   return CheckSnapshotList();
15641 }
15642
15643
15644 // ---------- new game button stuff -------------------------------------------
15645
15646 static struct
15647 {
15648   int graphic;
15649   struct XY *pos;
15650   int gadget_id;
15651   boolean *setup_value;
15652   boolean allowed_on_tape;
15653   boolean is_touch_button;
15654   char *infotext;
15655 } gamebutton_info[NUM_GAME_BUTTONS] =
15656 {
15657   {
15658     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15659     GAME_CTRL_ID_STOP,                          NULL,
15660     TRUE, FALSE,                                "stop game"
15661   },
15662   {
15663     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15664     GAME_CTRL_ID_PAUSE,                         NULL,
15665     TRUE, FALSE,                                "pause game"
15666   },
15667   {
15668     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15669     GAME_CTRL_ID_PLAY,                          NULL,
15670     TRUE, FALSE,                                "play game"
15671   },
15672   {
15673     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15674     GAME_CTRL_ID_UNDO,                          NULL,
15675     TRUE, FALSE,                                "undo step"
15676   },
15677   {
15678     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15679     GAME_CTRL_ID_REDO,                          NULL,
15680     TRUE, FALSE,                                "redo step"
15681   },
15682   {
15683     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15684     GAME_CTRL_ID_SAVE,                          NULL,
15685     TRUE, FALSE,                                "save game"
15686   },
15687   {
15688     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15689     GAME_CTRL_ID_PAUSE2,                        NULL,
15690     TRUE, FALSE,                                "pause game"
15691   },
15692   {
15693     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15694     GAME_CTRL_ID_LOAD,                          NULL,
15695     TRUE, FALSE,                                "load game"
15696   },
15697   {
15698     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15699     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15700     FALSE, FALSE,                               "stop game"
15701   },
15702   {
15703     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15704     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15705     FALSE, FALSE,                               "pause game"
15706   },
15707   {
15708     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15709     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15710     FALSE, FALSE,                               "play game"
15711   },
15712   {
15713     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
15714     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
15715     FALSE, TRUE,                                "stop game"
15716   },
15717   {
15718     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
15719     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
15720     FALSE, TRUE,                                "pause game"
15721   },
15722   {
15723     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15724     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15725     TRUE, FALSE,                                "background music on/off"
15726   },
15727   {
15728     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15729     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15730     TRUE, FALSE,                                "sound loops on/off"
15731   },
15732   {
15733     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15734     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15735     TRUE, FALSE,                                "normal sounds on/off"
15736   },
15737   {
15738     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15739     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15740     FALSE, FALSE,                               "background music on/off"
15741   },
15742   {
15743     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15744     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15745     FALSE, FALSE,                               "sound loops on/off"
15746   },
15747   {
15748     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15749     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15750     FALSE, FALSE,                               "normal sounds on/off"
15751   }
15752 };
15753
15754 void CreateGameButtons(void)
15755 {
15756   int i;
15757
15758   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15759   {
15760     int graphic = gamebutton_info[i].graphic;
15761     struct GraphicInfo *gfx = &graphic_info[graphic];
15762     struct XY *pos = gamebutton_info[i].pos;
15763     struct GadgetInfo *gi;
15764     int button_type;
15765     boolean checked;
15766     unsigned int event_mask;
15767     boolean is_touch_button = gamebutton_info[i].is_touch_button;
15768     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15769     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15770     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
15771     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
15772     int gd_x   = gfx->src_x;
15773     int gd_y   = gfx->src_y;
15774     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15775     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15776     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15777     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15778     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15779     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15780     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
15781     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
15782     int id = i;
15783
15784     if (gfx->bitmap == NULL)
15785     {
15786       game_gadget[id] = NULL;
15787
15788       continue;
15789     }
15790
15791     if (id == GAME_CTRL_ID_STOP ||
15792         id == GAME_CTRL_ID_PANEL_STOP ||
15793         id == GAME_CTRL_ID_TOUCH_STOP ||
15794         id == GAME_CTRL_ID_PLAY ||
15795         id == GAME_CTRL_ID_PANEL_PLAY ||
15796         id == GAME_CTRL_ID_SAVE ||
15797         id == GAME_CTRL_ID_LOAD)
15798     {
15799       button_type = GD_TYPE_NORMAL_BUTTON;
15800       checked = FALSE;
15801       event_mask = GD_EVENT_RELEASED;
15802     }
15803     else if (id == GAME_CTRL_ID_UNDO ||
15804              id == GAME_CTRL_ID_REDO)
15805     {
15806       button_type = GD_TYPE_NORMAL_BUTTON;
15807       checked = FALSE;
15808       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15809     }
15810     else
15811     {
15812       button_type = GD_TYPE_CHECK_BUTTON;
15813       checked = (gamebutton_info[i].setup_value != NULL ?
15814                  *gamebutton_info[i].setup_value : FALSE);
15815       event_mask = GD_EVENT_PRESSED;
15816     }
15817
15818     gi = CreateGadget(GDI_CUSTOM_ID, id,
15819                       GDI_IMAGE_ID, graphic,
15820                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15821                       GDI_X, base_x + x,
15822                       GDI_Y, base_y + y,
15823                       GDI_WIDTH, gfx->width,
15824                       GDI_HEIGHT, gfx->height,
15825                       GDI_TYPE, button_type,
15826                       GDI_STATE, GD_BUTTON_UNPRESSED,
15827                       GDI_CHECKED, checked,
15828                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15829                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15830                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15831                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15832                       GDI_DIRECT_DRAW, FALSE,
15833                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
15834                       GDI_EVENT_MASK, event_mask,
15835                       GDI_CALLBACK_ACTION, HandleGameButtons,
15836                       GDI_END);
15837
15838     if (gi == NULL)
15839       Fail("cannot create gadget");
15840
15841     game_gadget[id] = gi;
15842   }
15843 }
15844
15845 void FreeGameButtons(void)
15846 {
15847   int i;
15848
15849   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15850     FreeGadget(game_gadget[i]);
15851 }
15852
15853 static void UnmapGameButtonsAtSamePosition(int id)
15854 {
15855   int i;
15856
15857   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15858     if (i != id &&
15859         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15860         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15861       UnmapGadget(game_gadget[i]);
15862 }
15863
15864 static void UnmapGameButtonsAtSamePosition_All(void)
15865 {
15866   if (setup.show_snapshot_buttons)
15867   {
15868     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15869     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15870     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15871   }
15872   else
15873   {
15874     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15875     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15876     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15877
15878     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15879     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15880     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15881   }
15882 }
15883
15884 static void MapGameButtonsAtSamePosition(int id)
15885 {
15886   int i;
15887
15888   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15889     if (i != id &&
15890         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15891         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15892       MapGadget(game_gadget[i]);
15893
15894   UnmapGameButtonsAtSamePosition_All();
15895 }
15896
15897 void MapUndoRedoButtons(void)
15898 {
15899   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15900   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15901
15902   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15903   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15904 }
15905
15906 void UnmapUndoRedoButtons(void)
15907 {
15908   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15909   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15910
15911   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15912   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15913 }
15914
15915 void ModifyPauseButtons(void)
15916 {
15917   static int ids[] =
15918   {
15919     GAME_CTRL_ID_PAUSE,
15920     GAME_CTRL_ID_PAUSE2,
15921     GAME_CTRL_ID_PANEL_PAUSE,
15922     GAME_CTRL_ID_TOUCH_PAUSE,
15923     -1
15924   };
15925   int i;
15926
15927   for (i = 0; ids[i] > -1; i++)
15928     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
15929 }
15930
15931 static void MapGameButtonsExt(boolean on_tape)
15932 {
15933   int i;
15934
15935   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15936     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15937         i != GAME_CTRL_ID_UNDO &&
15938         i != GAME_CTRL_ID_REDO)
15939       MapGadget(game_gadget[i]);
15940
15941   UnmapGameButtonsAtSamePosition_All();
15942
15943   RedrawGameButtons();
15944 }
15945
15946 static void UnmapGameButtonsExt(boolean on_tape)
15947 {
15948   int i;
15949
15950   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15951     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15952       UnmapGadget(game_gadget[i]);
15953 }
15954
15955 static void RedrawGameButtonsExt(boolean on_tape)
15956 {
15957   int i;
15958
15959   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15960     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15961       RedrawGadget(game_gadget[i]);
15962 }
15963
15964 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
15965 {
15966   if (gi == NULL)
15967     return;
15968
15969   gi->checked = state;
15970 }
15971
15972 static void RedrawSoundButtonGadget(int id)
15973 {
15974   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
15975              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
15976              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
15977              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
15978              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
15979              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15980              id);
15981
15982   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15983   RedrawGadget(game_gadget[id2]);
15984 }
15985
15986 void MapGameButtons(void)
15987 {
15988   MapGameButtonsExt(FALSE);
15989 }
15990
15991 void UnmapGameButtons(void)
15992 {
15993   UnmapGameButtonsExt(FALSE);
15994 }
15995
15996 void RedrawGameButtons(void)
15997 {
15998   RedrawGameButtonsExt(FALSE);
15999 }
16000
16001 void MapGameButtonsOnTape(void)
16002 {
16003   MapGameButtonsExt(TRUE);
16004 }
16005
16006 void UnmapGameButtonsOnTape(void)
16007 {
16008   UnmapGameButtonsExt(TRUE);
16009 }
16010
16011 void RedrawGameButtonsOnTape(void)
16012 {
16013   RedrawGameButtonsExt(TRUE);
16014 }
16015
16016 static void GameUndoRedoExt(void)
16017 {
16018   ClearPlayerAction();
16019
16020   tape.pausing = TRUE;
16021
16022   RedrawPlayfield();
16023   UpdateAndDisplayGameControlValues();
16024
16025   DrawCompleteVideoDisplay();
16026   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16027   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16028   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16029
16030   BackToFront();
16031 }
16032
16033 static void GameUndo(int steps)
16034 {
16035   if (!CheckEngineSnapshotList())
16036     return;
16037
16038   LoadEngineSnapshot_Undo(steps);
16039
16040   GameUndoRedoExt();
16041 }
16042
16043 static void GameRedo(int steps)
16044 {
16045   if (!CheckEngineSnapshotList())
16046     return;
16047
16048   LoadEngineSnapshot_Redo(steps);
16049
16050   GameUndoRedoExt();
16051 }
16052
16053 static void HandleGameButtonsExt(int id, int button)
16054 {
16055   static boolean game_undo_executed = FALSE;
16056   int steps = BUTTON_STEPSIZE(button);
16057   boolean handle_game_buttons =
16058     (game_status == GAME_MODE_PLAYING ||
16059      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16060
16061   if (!handle_game_buttons)
16062     return;
16063
16064   switch (id)
16065   {
16066     case GAME_CTRL_ID_STOP:
16067     case GAME_CTRL_ID_PANEL_STOP:
16068     case GAME_CTRL_ID_TOUCH_STOP:
16069       if (game_status == GAME_MODE_MAIN)
16070         break;
16071
16072       if (tape.playing)
16073         TapeStop();
16074       else
16075         RequestQuitGame(TRUE);
16076
16077       break;
16078
16079     case GAME_CTRL_ID_PAUSE:
16080     case GAME_CTRL_ID_PAUSE2:
16081     case GAME_CTRL_ID_PANEL_PAUSE:
16082     case GAME_CTRL_ID_TOUCH_PAUSE:
16083       if (network.enabled && game_status == GAME_MODE_PLAYING)
16084       {
16085         if (tape.pausing)
16086           SendToServer_ContinuePlaying();
16087         else
16088           SendToServer_PausePlaying();
16089       }
16090       else
16091         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16092
16093       game_undo_executed = FALSE;
16094
16095       break;
16096
16097     case GAME_CTRL_ID_PLAY:
16098     case GAME_CTRL_ID_PANEL_PLAY:
16099       if (game_status == GAME_MODE_MAIN)
16100       {
16101         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16102       }
16103       else if (tape.pausing)
16104       {
16105         if (network.enabled)
16106           SendToServer_ContinuePlaying();
16107         else
16108           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16109       }
16110       break;
16111
16112     case GAME_CTRL_ID_UNDO:
16113       // Important: When using "save snapshot when collecting an item" mode,
16114       // load last (current) snapshot for first "undo" after pressing "pause"
16115       // (else the last-but-one snapshot would be loaded, because the snapshot
16116       // pointer already points to the last snapshot when pressing "pause",
16117       // which is fine for "every step/move" mode, but not for "every collect")
16118       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16119           !game_undo_executed)
16120         steps--;
16121
16122       game_undo_executed = TRUE;
16123
16124       GameUndo(steps);
16125       break;
16126
16127     case GAME_CTRL_ID_REDO:
16128       GameRedo(steps);
16129       break;
16130
16131     case GAME_CTRL_ID_SAVE:
16132       TapeQuickSave();
16133       break;
16134
16135     case GAME_CTRL_ID_LOAD:
16136       TapeQuickLoad();
16137       break;
16138
16139     case SOUND_CTRL_ID_MUSIC:
16140     case SOUND_CTRL_ID_PANEL_MUSIC:
16141       if (setup.sound_music)
16142       { 
16143         setup.sound_music = FALSE;
16144
16145         FadeMusic();
16146       }
16147       else if (audio.music_available)
16148       { 
16149         setup.sound = setup.sound_music = TRUE;
16150
16151         SetAudioMode(setup.sound);
16152
16153         if (game_status == GAME_MODE_PLAYING)
16154           PlayLevelMusic();
16155       }
16156
16157       RedrawSoundButtonGadget(id);
16158
16159       break;
16160
16161     case SOUND_CTRL_ID_LOOPS:
16162     case SOUND_CTRL_ID_PANEL_LOOPS:
16163       if (setup.sound_loops)
16164         setup.sound_loops = FALSE;
16165       else if (audio.loops_available)
16166       {
16167         setup.sound = setup.sound_loops = TRUE;
16168
16169         SetAudioMode(setup.sound);
16170       }
16171
16172       RedrawSoundButtonGadget(id);
16173
16174       break;
16175
16176     case SOUND_CTRL_ID_SIMPLE:
16177     case SOUND_CTRL_ID_PANEL_SIMPLE:
16178       if (setup.sound_simple)
16179         setup.sound_simple = FALSE;
16180       else if (audio.sound_available)
16181       {
16182         setup.sound = setup.sound_simple = TRUE;
16183
16184         SetAudioMode(setup.sound);
16185       }
16186
16187       RedrawSoundButtonGadget(id);
16188
16189       break;
16190
16191     default:
16192       break;
16193   }
16194 }
16195
16196 static void HandleGameButtons(struct GadgetInfo *gi)
16197 {
16198   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16199 }
16200
16201 void HandleSoundButtonKeys(Key key)
16202 {
16203   if (key == setup.shortcut.sound_simple)
16204     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16205   else if (key == setup.shortcut.sound_loops)
16206     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16207   else if (key == setup.shortcut.sound_music)
16208     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16209 }