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