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