changed non-fatal error handling to use new error functions
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 // DEBUG SETTINGS
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 // EXPERIMENTAL STUFF
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 // EXPERIMENTAL STUFF
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 // for DigField()
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 // for MovePlayer()
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 // for ScrollPlayer()
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 // for Bang()/Explode()
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 // game panel display and control definitions
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 // values for delayed check of falling and moving elements and for collision
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 // values for initial player move delay (initial delay counter value)
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 // values for player movement speed (which is in fact a delay value)
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 // values for scroll positions
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 // values for other actions
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
883                                  RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
886                                  RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
888                                  RND((c)->delay_random))
889
890
891 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
892          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
893
894 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
895         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
896          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
897          (be) + (e) - EL_SELF)
898
899 #define GET_PLAYER_FROM_BITS(p)                                         \
900         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
901
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
903         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
904          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
905          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
906          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
907          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
908          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
909          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
910          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
911          (e))
912
913 #define CAN_GROW_INTO(e)                                                \
914         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
915
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
917                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
918                                         (condition)))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (CAN_MOVE_INTO_ACID(e) &&       \
923                                          Tile[x][y] == EL_ACID) ||      \
924                                         (condition)))
925
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
927                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
928                                         (CAN_MOVE_INTO_ACID(e) &&       \
929                                          Tile[x][y] == EL_ACID) ||      \
930                                         (condition)))
931
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
933                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
934                                         (condition) ||                  \
935                                         (CAN_MOVE_INTO_ACID(e) &&       \
936                                          Tile[x][y] == EL_ACID) ||      \
937                                         (DONT_COLLIDE_WITH(e) &&        \
938                                          IS_PLAYER(x, y) &&             \
939                                          !PLAYER_ENEMY_PROTECTED(x, y))))
940
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
942         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
943
944 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
945         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
946
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
948         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Tile[x][y] == EL_EMC_PLANT)
949
950 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
951         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Tile[x][y]) || \
952                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
953
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
955         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
956
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
958         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Tile[x][y] == EL_DIAMOND)
959
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
961         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Tile[x][y]))
962
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
964         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Tile[x][y]))
965
966 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
967         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Tile[x][y]))
968
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
970         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Tile[x][y] == EL_EXIT_OPEN || \
971                                                  Tile[x][y] == EL_EM_EXIT_OPEN || \
972                                                  Tile[x][y] == EL_STEEL_EXIT_OPEN || \
973                                                  Tile[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974                                                  IS_FOOD_PENGUIN(Tile[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
976         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
977
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
979         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
980
981 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
982         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
983
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
985         (IN_LEV_FIELD(x, y) && (Tile[x][y] == EL_EMC_SPRING_BUMPER ||   \
986                                 Tile[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
987
988 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
989
990 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
991                 (!IS_PLAYER(x, y) &&                                    \
992                  IS_EQUAL_OR_IN_GROUP(Tile[x][y], MOVE_ENTER_EL(e)))
993
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
995         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
996
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
999
1000 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1004
1005 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1006
1007 // game button identifiers
1008 #define GAME_CTRL_ID_STOP               0
1009 #define GAME_CTRL_ID_PAUSE              1
1010 #define GAME_CTRL_ID_PLAY               2
1011 #define GAME_CTRL_ID_UNDO               3
1012 #define GAME_CTRL_ID_REDO               4
1013 #define GAME_CTRL_ID_SAVE               5
1014 #define GAME_CTRL_ID_PAUSE2             6
1015 #define GAME_CTRL_ID_LOAD               7
1016 #define GAME_CTRL_ID_PANEL_STOP         8
1017 #define GAME_CTRL_ID_PANEL_PAUSE        9
1018 #define GAME_CTRL_ID_PANEL_PLAY         10
1019 #define GAME_CTRL_ID_TOUCH_STOP         11
1020 #define GAME_CTRL_ID_TOUCH_PAUSE        12
1021 #define SOUND_CTRL_ID_MUSIC             13
1022 #define SOUND_CTRL_ID_LOOPS             14
1023 #define SOUND_CTRL_ID_SIMPLE            15
1024 #define SOUND_CTRL_ID_PANEL_MUSIC       16
1025 #define SOUND_CTRL_ID_PANEL_LOOPS       17
1026 #define SOUND_CTRL_ID_PANEL_SIMPLE      18
1027
1028 #define NUM_GAME_BUTTONS                19
1029
1030
1031 // forward declaration for internal use
1032
1033 static void CreateField(int, int, int);
1034
1035 static void ResetGfxAnimation(int, int);
1036
1037 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1038 static void AdvanceFrameAndPlayerCounters(int);
1039
1040 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1041 static boolean MovePlayer(struct PlayerInfo *, int, int);
1042 static void ScrollPlayer(struct PlayerInfo *, int);
1043 static void ScrollScreen(struct PlayerInfo *, int);
1044
1045 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1046 static boolean DigFieldByCE(int, int, int);
1047 static boolean SnapField(struct PlayerInfo *, int, int);
1048 static boolean DropElement(struct PlayerInfo *);
1049
1050 static void InitBeltMovement(void);
1051 static void CloseAllOpenTimegates(void);
1052 static void CheckGravityMovement(struct PlayerInfo *);
1053 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1054 static void KillPlayerUnlessEnemyProtected(int, int);
1055 static void KillPlayerUnlessExplosionProtected(int, int);
1056
1057 static void TestIfPlayerTouchesCustomElement(int, int);
1058 static void TestIfElementTouchesCustomElement(int, int);
1059 static void TestIfElementHitsCustomElement(int, int, int);
1060
1061 static void HandleElementChange(int, int, int);
1062 static void ExecuteCustomElementAction(int, int, int, int);
1063 static boolean ChangeElement(int, int, int, int);
1064
1065 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1066 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1067         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1068 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1069         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1070 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1071         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1072 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1073         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1074
1075 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1076 #define CheckElementChange(x, y, e, te, ev)                             \
1077         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1078 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1079         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1080 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1081         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1082
1083 static void PlayLevelSound(int, int, int);
1084 static void PlayLevelSoundNearest(int, int, int);
1085 static void PlayLevelSoundAction(int, int, int);
1086 static void PlayLevelSoundElementAction(int, int, int, int);
1087 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1088 static void PlayLevelSoundActionIfLoop(int, int, int);
1089 static void StopLevelSoundActionIfLoop(int, int, int);
1090 static void PlayLevelMusic(void);
1091 static void FadeLevelSoundsAndMusic(void);
1092
1093 static void HandleGameButtons(struct GadgetInfo *);
1094
1095 int AmoebaNeighbourNr(int, int);
1096 void AmoebaToDiamond(int, int);
1097 void ContinueMoving(int, int);
1098 void Bang(int, int);
1099 void InitMovDir(int, int);
1100 void InitAmoebaNr(int, int);
1101 int NewHiScore(int);
1102
1103 void TestIfGoodThingHitsBadThing(int, int, int);
1104 void TestIfBadThingHitsGoodThing(int, int, int);
1105 void TestIfPlayerTouchesBadThing(int, int);
1106 void TestIfPlayerRunsIntoBadThing(int, int, int);
1107 void TestIfBadThingTouchesPlayer(int, int);
1108 void TestIfBadThingRunsIntoPlayer(int, int, int);
1109 void TestIfFriendTouchesBadThing(int, int);
1110 void TestIfBadThingTouchesFriend(int, int);
1111 void TestIfBadThingTouchesOtherBadThing(int, int);
1112 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1113
1114 void KillPlayer(struct PlayerInfo *);
1115 void BuryPlayer(struct PlayerInfo *);
1116 void RemovePlayer(struct PlayerInfo *);
1117 void ExitPlayer(struct PlayerInfo *);
1118
1119 static int getInvisibleActiveFromInvisibleElement(int);
1120 static int getInvisibleFromInvisibleActiveElement(int);
1121
1122 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1123
1124 // for detection of endless loops, caused by custom element programming
1125 // (using maximal playfield width x 10 is just a rough approximation)
1126 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1127
1128 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1129 {                                                                       \
1130   if (recursion_loop_detected)                                          \
1131     return (rc);                                                        \
1132                                                                         \
1133   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1134   {                                                                     \
1135     recursion_loop_detected = TRUE;                                     \
1136     recursion_loop_element = (e);                                       \
1137   }                                                                     \
1138                                                                         \
1139   recursion_loop_depth++;                                               \
1140 }
1141
1142 #define RECURSION_LOOP_DETECTION_END()                                  \
1143 {                                                                       \
1144   recursion_loop_depth--;                                               \
1145 }
1146
1147 static int recursion_loop_depth;
1148 static boolean recursion_loop_detected;
1149 static boolean recursion_loop_element;
1150
1151 static int map_player_action[MAX_PLAYERS];
1152
1153
1154 // ----------------------------------------------------------------------------
1155 // definition of elements that automatically change to other elements after
1156 // a specified time, eventually calling a function when changing
1157 // ----------------------------------------------------------------------------
1158
1159 // forward declaration for changer functions
1160 static void InitBuggyBase(int, int);
1161 static void WarnBuggyBase(int, int);
1162
1163 static void InitTrap(int, int);
1164 static void ActivateTrap(int, int);
1165 static void ChangeActiveTrap(int, int);
1166
1167 static void InitRobotWheel(int, int);
1168 static void RunRobotWheel(int, int);
1169 static void StopRobotWheel(int, int);
1170
1171 static void InitTimegateWheel(int, int);
1172 static void RunTimegateWheel(int, int);
1173
1174 static void InitMagicBallDelay(int, int);
1175 static void ActivateMagicBall(int, int);
1176
1177 struct ChangingElementInfo
1178 {
1179   int element;
1180   int target_element;
1181   int change_delay;
1182   void (*pre_change_function)(int x, int y);
1183   void (*change_function)(int x, int y);
1184   void (*post_change_function)(int x, int y);
1185 };
1186
1187 static struct ChangingElementInfo change_delay_list[] =
1188 {
1189   {
1190     EL_NUT_BREAKING,
1191     EL_EMERALD,
1192     6,
1193     NULL,
1194     NULL,
1195     NULL
1196   },
1197   {
1198     EL_PEARL_BREAKING,
1199     EL_EMPTY,
1200     8,
1201     NULL,
1202     NULL,
1203     NULL
1204   },
1205   {
1206     EL_EXIT_OPENING,
1207     EL_EXIT_OPEN,
1208     29,
1209     NULL,
1210     NULL,
1211     NULL
1212   },
1213   {
1214     EL_EXIT_CLOSING,
1215     EL_EXIT_CLOSED,
1216     29,
1217     NULL,
1218     NULL,
1219     NULL
1220   },
1221   {
1222     EL_STEEL_EXIT_OPENING,
1223     EL_STEEL_EXIT_OPEN,
1224     29,
1225     NULL,
1226     NULL,
1227     NULL
1228   },
1229   {
1230     EL_STEEL_EXIT_CLOSING,
1231     EL_STEEL_EXIT_CLOSED,
1232     29,
1233     NULL,
1234     NULL,
1235     NULL
1236   },
1237   {
1238     EL_EM_EXIT_OPENING,
1239     EL_EM_EXIT_OPEN,
1240     29,
1241     NULL,
1242     NULL,
1243     NULL
1244   },
1245   {
1246     EL_EM_EXIT_CLOSING,
1247     EL_EMPTY,
1248     29,
1249     NULL,
1250     NULL,
1251     NULL
1252   },
1253   {
1254     EL_EM_STEEL_EXIT_OPENING,
1255     EL_EM_STEEL_EXIT_OPEN,
1256     29,
1257     NULL,
1258     NULL,
1259     NULL
1260   },
1261   {
1262     EL_EM_STEEL_EXIT_CLOSING,
1263     EL_STEELWALL,
1264     29,
1265     NULL,
1266     NULL,
1267     NULL
1268   },
1269   {
1270     EL_SP_EXIT_OPENING,
1271     EL_SP_EXIT_OPEN,
1272     29,
1273     NULL,
1274     NULL,
1275     NULL
1276   },
1277   {
1278     EL_SP_EXIT_CLOSING,
1279     EL_SP_EXIT_CLOSED,
1280     29,
1281     NULL,
1282     NULL,
1283     NULL
1284   },
1285   {
1286     EL_SWITCHGATE_OPENING,
1287     EL_SWITCHGATE_OPEN,
1288     29,
1289     NULL,
1290     NULL,
1291     NULL
1292   },
1293   {
1294     EL_SWITCHGATE_CLOSING,
1295     EL_SWITCHGATE_CLOSED,
1296     29,
1297     NULL,
1298     NULL,
1299     NULL
1300   },
1301   {
1302     EL_TIMEGATE_OPENING,
1303     EL_TIMEGATE_OPEN,
1304     29,
1305     NULL,
1306     NULL,
1307     NULL
1308   },
1309   {
1310     EL_TIMEGATE_CLOSING,
1311     EL_TIMEGATE_CLOSED,
1312     29,
1313     NULL,
1314     NULL,
1315     NULL
1316   },
1317
1318   {
1319     EL_ACID_SPLASH_LEFT,
1320     EL_EMPTY,
1321     8,
1322     NULL,
1323     NULL,
1324     NULL
1325   },
1326   {
1327     EL_ACID_SPLASH_RIGHT,
1328     EL_EMPTY,
1329     8,
1330     NULL,
1331     NULL,
1332     NULL
1333   },
1334   {
1335     EL_SP_BUGGY_BASE,
1336     EL_SP_BUGGY_BASE_ACTIVATING,
1337     0,
1338     InitBuggyBase,
1339     NULL,
1340     NULL
1341   },
1342   {
1343     EL_SP_BUGGY_BASE_ACTIVATING,
1344     EL_SP_BUGGY_BASE_ACTIVE,
1345     0,
1346     InitBuggyBase,
1347     NULL,
1348     NULL
1349   },
1350   {
1351     EL_SP_BUGGY_BASE_ACTIVE,
1352     EL_SP_BUGGY_BASE,
1353     0,
1354     InitBuggyBase,
1355     WarnBuggyBase,
1356     NULL
1357   },
1358   {
1359     EL_TRAP,
1360     EL_TRAP_ACTIVE,
1361     0,
1362     InitTrap,
1363     NULL,
1364     ActivateTrap
1365   },
1366   {
1367     EL_TRAP_ACTIVE,
1368     EL_TRAP,
1369     31,
1370     NULL,
1371     ChangeActiveTrap,
1372     NULL
1373   },
1374   {
1375     EL_ROBOT_WHEEL_ACTIVE,
1376     EL_ROBOT_WHEEL,
1377     0,
1378     InitRobotWheel,
1379     RunRobotWheel,
1380     StopRobotWheel
1381   },
1382   {
1383     EL_TIMEGATE_SWITCH_ACTIVE,
1384     EL_TIMEGATE_SWITCH,
1385     0,
1386     InitTimegateWheel,
1387     RunTimegateWheel,
1388     NULL
1389   },
1390   {
1391     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1392     EL_DC_TIMEGATE_SWITCH,
1393     0,
1394     InitTimegateWheel,
1395     RunTimegateWheel,
1396     NULL
1397   },
1398   {
1399     EL_EMC_MAGIC_BALL_ACTIVE,
1400     EL_EMC_MAGIC_BALL_ACTIVE,
1401     0,
1402     InitMagicBallDelay,
1403     NULL,
1404     ActivateMagicBall
1405   },
1406   {
1407     EL_EMC_SPRING_BUMPER_ACTIVE,
1408     EL_EMC_SPRING_BUMPER,
1409     8,
1410     NULL,
1411     NULL,
1412     NULL
1413   },
1414   {
1415     EL_DIAGONAL_SHRINKING,
1416     EL_UNDEFINED,
1417     0,
1418     NULL,
1419     NULL,
1420     NULL
1421   },
1422   {
1423     EL_DIAGONAL_GROWING,
1424     EL_UNDEFINED,
1425     0,
1426     NULL,
1427     NULL,
1428     NULL,
1429   },
1430
1431   {
1432     EL_UNDEFINED,
1433     EL_UNDEFINED,
1434     -1,
1435     NULL,
1436     NULL,
1437     NULL
1438   }
1439 };
1440
1441 struct
1442 {
1443   int element;
1444   int push_delay_fixed, push_delay_random;
1445 }
1446 push_delay_list[] =
1447 {
1448   { EL_SPRING,                  0, 0 },
1449   { EL_BALLOON,                 0, 0 },
1450
1451   { EL_SOKOBAN_OBJECT,          2, 0 },
1452   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1453   { EL_SATELLITE,               2, 0 },
1454   { EL_SP_DISK_YELLOW,          2, 0 },
1455
1456   { EL_UNDEFINED,               0, 0 },
1457 };
1458
1459 struct
1460 {
1461   int element;
1462   int move_stepsize;
1463 }
1464 move_stepsize_list[] =
1465 {
1466   { EL_AMOEBA_DROP,             2 },
1467   { EL_AMOEBA_DROPPING,         2 },
1468   { EL_QUICKSAND_FILLING,       1 },
1469   { EL_QUICKSAND_EMPTYING,      1 },
1470   { EL_QUICKSAND_FAST_FILLING,  2 },
1471   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1472   { EL_MAGIC_WALL_FILLING,      2 },
1473   { EL_MAGIC_WALL_EMPTYING,     2 },
1474   { EL_BD_MAGIC_WALL_FILLING,   2 },
1475   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1476   { EL_DC_MAGIC_WALL_FILLING,   2 },
1477   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1478
1479   { EL_UNDEFINED,               0 },
1480 };
1481
1482 struct
1483 {
1484   int element;
1485   int count;
1486 }
1487 collect_count_list[] =
1488 {
1489   { EL_EMERALD,                 1 },
1490   { EL_BD_DIAMOND,              1 },
1491   { EL_EMERALD_YELLOW,          1 },
1492   { EL_EMERALD_RED,             1 },
1493   { EL_EMERALD_PURPLE,          1 },
1494   { EL_DIAMOND,                 3 },
1495   { EL_SP_INFOTRON,             1 },
1496   { EL_PEARL,                   5 },
1497   { EL_CRYSTAL,                 8 },
1498
1499   { EL_UNDEFINED,               0 },
1500 };
1501
1502 struct
1503 {
1504   int element;
1505   int direction;
1506 }
1507 access_direction_list[] =
1508 {
1509   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1510   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1511   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1512   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1513   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1514   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1515   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1516   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1517   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1518   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1519   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1520
1521   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1522   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1523   { EL_SP_PORT_UP,                                                   MV_DOWN },
1524   { EL_SP_PORT_DOWN,                                         MV_UP           },
1525   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1526   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1527   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1528   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1529   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1530   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1531   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1532   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1533   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1534   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1535   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1536   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1537   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1538   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1539   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1540
1541   { EL_UNDEFINED,                       MV_NONE                              }
1542 };
1543
1544 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1545
1546 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1547 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1548 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1549                                  IS_JUST_CHANGING(x, y))
1550
1551 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1552
1553 // static variables for playfield scan mode (scanning forward or backward)
1554 static int playfield_scan_start_x = 0;
1555 static int playfield_scan_start_y = 0;
1556 static int playfield_scan_delta_x = 1;
1557 static int playfield_scan_delta_y = 1;
1558
1559 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1560                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1561                                      (y) += playfield_scan_delta_y)     \
1562                                 for ((x) = playfield_scan_start_x;      \
1563                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1564                                      (x) += playfield_scan_delta_x)
1565
1566 #ifdef DEBUG
1567 void DEBUG_SetMaximumDynamite(void)
1568 {
1569   int i;
1570
1571   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1572     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1573       local_player->inventory_element[local_player->inventory_size++] =
1574         EL_DYNAMITE;
1575 }
1576 #endif
1577
1578 static void InitPlayfieldScanModeVars(void)
1579 {
1580   if (game.use_reverse_scan_direction)
1581   {
1582     playfield_scan_start_x = lev_fieldx - 1;
1583     playfield_scan_start_y = lev_fieldy - 1;
1584
1585     playfield_scan_delta_x = -1;
1586     playfield_scan_delta_y = -1;
1587   }
1588   else
1589   {
1590     playfield_scan_start_x = 0;
1591     playfield_scan_start_y = 0;
1592
1593     playfield_scan_delta_x = 1;
1594     playfield_scan_delta_y = 1;
1595   }
1596 }
1597
1598 static void InitPlayfieldScanMode(int mode)
1599 {
1600   game.use_reverse_scan_direction =
1601     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1602
1603   InitPlayfieldScanModeVars();
1604 }
1605
1606 static int get_move_delay_from_stepsize(int move_stepsize)
1607 {
1608   move_stepsize =
1609     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1610
1611   // make sure that stepsize value is always a power of 2
1612   move_stepsize = (1 << log_2(move_stepsize));
1613
1614   return TILEX / move_stepsize;
1615 }
1616
1617 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1618                                boolean init_game)
1619 {
1620   int player_nr = player->index_nr;
1621   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1622   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1623
1624   // do no immediately change move delay -- the player might just be moving
1625   player->move_delay_value_next = move_delay;
1626
1627   // information if player can move must be set separately
1628   player->cannot_move = cannot_move;
1629
1630   if (init_game)
1631   {
1632     player->move_delay       = game.initial_move_delay[player_nr];
1633     player->move_delay_value = game.initial_move_delay_value[player_nr];
1634
1635     player->move_delay_value_next = -1;
1636
1637     player->move_delay_reset_counter = 0;
1638   }
1639 }
1640
1641 void GetPlayerConfig(void)
1642 {
1643   GameFrameDelay = setup.game_frame_delay;
1644
1645   if (!audio.sound_available)
1646     setup.sound_simple = FALSE;
1647
1648   if (!audio.loops_available)
1649     setup.sound_loops = FALSE;
1650
1651   if (!audio.music_available)
1652     setup.sound_music = FALSE;
1653
1654   if (!video.fullscreen_available)
1655     setup.fullscreen = FALSE;
1656
1657   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1658
1659   SetAudioMode(setup.sound);
1660 }
1661
1662 int GetElementFromGroupElement(int element)
1663 {
1664   if (IS_GROUP_ELEMENT(element))
1665   {
1666     struct ElementGroupInfo *group = element_info[element].group;
1667     int last_anim_random_frame = gfx.anim_random_frame;
1668     int element_pos;
1669
1670     if (group->choice_mode == ANIM_RANDOM)
1671       gfx.anim_random_frame = RND(group->num_elements_resolved);
1672
1673     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1674                                     group->choice_mode, 0,
1675                                     group->choice_pos);
1676
1677     if (group->choice_mode == ANIM_RANDOM)
1678       gfx.anim_random_frame = last_anim_random_frame;
1679
1680     group->choice_pos++;
1681
1682     element = group->element_resolved[element_pos];
1683   }
1684
1685   return element;
1686 }
1687
1688 static void IncrementSokobanFieldsNeeded(void)
1689 {
1690   if (level.sb_fields_needed)
1691     game.sokoban_fields_still_needed++;
1692 }
1693
1694 static void IncrementSokobanObjectsNeeded(void)
1695 {
1696   if (level.sb_objects_needed)
1697     game.sokoban_objects_still_needed++;
1698 }
1699
1700 static void DecrementSokobanFieldsNeeded(void)
1701 {
1702   if (game.sokoban_fields_still_needed > 0)
1703     game.sokoban_fields_still_needed--;
1704 }
1705
1706 static void DecrementSokobanObjectsNeeded(void)
1707 {
1708   if (game.sokoban_objects_still_needed > 0)
1709     game.sokoban_objects_still_needed--;
1710 }
1711
1712 static void InitPlayerField(int x, int y, int element, boolean init_game)
1713 {
1714   if (element == EL_SP_MURPHY)
1715   {
1716     if (init_game)
1717     {
1718       if (stored_player[0].present)
1719       {
1720         Tile[x][y] = EL_SP_MURPHY_CLONE;
1721
1722         return;
1723       }
1724       else
1725       {
1726         stored_player[0].initial_element = element;
1727         stored_player[0].use_murphy = TRUE;
1728
1729         if (!level.use_artwork_element[0])
1730           stored_player[0].artwork_element = EL_SP_MURPHY;
1731       }
1732
1733       Tile[x][y] = EL_PLAYER_1;
1734     }
1735   }
1736
1737   if (init_game)
1738   {
1739     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1740     int jx = player->jx, jy = player->jy;
1741
1742     player->present = TRUE;
1743
1744     player->block_last_field = (element == EL_SP_MURPHY ?
1745                                 level.sp_block_last_field :
1746                                 level.block_last_field);
1747
1748     // ---------- initialize player's last field block delay ------------------
1749
1750     // always start with reliable default value (no adjustment needed)
1751     player->block_delay_adjustment = 0;
1752
1753     // special case 1: in Supaplex, Murphy blocks last field one more frame
1754     if (player->block_last_field && element == EL_SP_MURPHY)
1755       player->block_delay_adjustment = 1;
1756
1757     // special case 2: in game engines before 3.1.1, blocking was different
1758     if (game.use_block_last_field_bug)
1759       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1760
1761     if (!network.enabled || player->connected_network)
1762     {
1763       player->active = TRUE;
1764
1765       // remove potentially duplicate players
1766       if (StorePlayer[jx][jy] == Tile[x][y])
1767         StorePlayer[jx][jy] = 0;
1768
1769       StorePlayer[x][y] = Tile[x][y];
1770
1771 #if DEBUG_INIT_PLAYER
1772       Debug("game:init:player", "- player element %d activated",
1773             player->element_nr);
1774       Debug("game:init:player", "  (local player is %d and currently %s)",
1775             local_player->element_nr,
1776             local_player->active ? "active" : "not active");
1777     }
1778 #endif
1779
1780     Tile[x][y] = EL_EMPTY;
1781
1782     player->jx = player->last_jx = x;
1783     player->jy = player->last_jy = y;
1784   }
1785
1786   // always check if player was just killed and should be reanimated
1787   {
1788     int player_nr = GET_PLAYER_NR(element);
1789     struct PlayerInfo *player = &stored_player[player_nr];
1790
1791     if (player->active && player->killed)
1792       player->reanimated = TRUE; // if player was just killed, reanimate him
1793   }
1794 }
1795
1796 static void InitField(int x, int y, boolean init_game)
1797 {
1798   int element = Tile[x][y];
1799
1800   switch (element)
1801   {
1802     case EL_SP_MURPHY:
1803     case EL_PLAYER_1:
1804     case EL_PLAYER_2:
1805     case EL_PLAYER_3:
1806     case EL_PLAYER_4:
1807       InitPlayerField(x, y, element, init_game);
1808       break;
1809
1810     case EL_SOKOBAN_FIELD_PLAYER:
1811       element = Tile[x][y] = EL_PLAYER_1;
1812       InitField(x, y, init_game);
1813
1814       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1815       InitField(x, y, init_game);
1816       break;
1817
1818     case EL_SOKOBAN_FIELD_EMPTY:
1819       IncrementSokobanFieldsNeeded();
1820       break;
1821
1822     case EL_SOKOBAN_OBJECT:
1823       IncrementSokobanObjectsNeeded();
1824       break;
1825
1826     case EL_STONEBLOCK:
1827       if (x < lev_fieldx-1 && Tile[x+1][y] == EL_ACID)
1828         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1829       else if (x > 0 && Tile[x-1][y] == EL_ACID)
1830         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1831       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPLEFT)
1832         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1833       else if (y > 0 && Tile[x][y-1] == EL_ACID)
1834         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1835       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1836         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1837       break;
1838
1839     case EL_BUG:
1840     case EL_BUG_RIGHT:
1841     case EL_BUG_UP:
1842     case EL_BUG_LEFT:
1843     case EL_BUG_DOWN:
1844     case EL_SPACESHIP:
1845     case EL_SPACESHIP_RIGHT:
1846     case EL_SPACESHIP_UP:
1847     case EL_SPACESHIP_LEFT:
1848     case EL_SPACESHIP_DOWN:
1849     case EL_BD_BUTTERFLY:
1850     case EL_BD_BUTTERFLY_RIGHT:
1851     case EL_BD_BUTTERFLY_UP:
1852     case EL_BD_BUTTERFLY_LEFT:
1853     case EL_BD_BUTTERFLY_DOWN:
1854     case EL_BD_FIREFLY:
1855     case EL_BD_FIREFLY_RIGHT:
1856     case EL_BD_FIREFLY_UP:
1857     case EL_BD_FIREFLY_LEFT:
1858     case EL_BD_FIREFLY_DOWN:
1859     case EL_PACMAN_RIGHT:
1860     case EL_PACMAN_UP:
1861     case EL_PACMAN_LEFT:
1862     case EL_PACMAN_DOWN:
1863     case EL_YAMYAM:
1864     case EL_YAMYAM_LEFT:
1865     case EL_YAMYAM_RIGHT:
1866     case EL_YAMYAM_UP:
1867     case EL_YAMYAM_DOWN:
1868     case EL_DARK_YAMYAM:
1869     case EL_ROBOT:
1870     case EL_PACMAN:
1871     case EL_SP_SNIKSNAK:
1872     case EL_SP_ELECTRON:
1873     case EL_MOLE:
1874     case EL_MOLE_LEFT:
1875     case EL_MOLE_RIGHT:
1876     case EL_MOLE_UP:
1877     case EL_MOLE_DOWN:
1878     case EL_SPRING_LEFT:
1879     case EL_SPRING_RIGHT:
1880       InitMovDir(x, y);
1881       break;
1882
1883     case EL_AMOEBA_FULL:
1884     case EL_BD_AMOEBA:
1885       InitAmoebaNr(x, y);
1886       break;
1887
1888     case EL_AMOEBA_DROP:
1889       if (y == lev_fieldy - 1)
1890       {
1891         Tile[x][y] = EL_AMOEBA_GROWING;
1892         Store[x][y] = EL_AMOEBA_WET;
1893       }
1894       break;
1895
1896     case EL_DYNAMITE_ACTIVE:
1897     case EL_SP_DISK_RED_ACTIVE:
1898     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1899     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1900     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1901     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1902       MovDelay[x][y] = 96;
1903       break;
1904
1905     case EL_EM_DYNAMITE_ACTIVE:
1906       MovDelay[x][y] = 32;
1907       break;
1908
1909     case EL_LAMP:
1910       game.lights_still_needed++;
1911       break;
1912
1913     case EL_PENGUIN:
1914       game.friends_still_needed++;
1915       break;
1916
1917     case EL_PIG:
1918     case EL_DRAGON:
1919       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1920       break;
1921
1922     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1923     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1924     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1925     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1926     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1927     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1928     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1929     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1930     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1931     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1932     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1933     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1934       if (init_game)
1935       {
1936         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1937         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1938         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1939
1940         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1941         {
1942           game.belt_dir[belt_nr] = belt_dir;
1943           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1944         }
1945         else    // more than one switch -- set it like the first switch
1946         {
1947           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1948         }
1949       }
1950       break;
1951
1952     case EL_LIGHT_SWITCH_ACTIVE:
1953       if (init_game)
1954         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1955       break;
1956
1957     case EL_INVISIBLE_STEELWALL:
1958     case EL_INVISIBLE_WALL:
1959     case EL_INVISIBLE_SAND:
1960       if (game.light_time_left > 0 ||
1961           game.lenses_time_left > 0)
1962         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1963       break;
1964
1965     case EL_EMC_MAGIC_BALL:
1966       if (game.ball_active)
1967         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1968       break;
1969
1970     case EL_EMC_MAGIC_BALL_SWITCH:
1971       if (game.ball_active)
1972         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1973       break;
1974
1975     case EL_TRIGGER_PLAYER:
1976     case EL_TRIGGER_ELEMENT:
1977     case EL_TRIGGER_CE_VALUE:
1978     case EL_TRIGGER_CE_SCORE:
1979     case EL_SELF:
1980     case EL_ANY_ELEMENT:
1981     case EL_CURRENT_CE_VALUE:
1982     case EL_CURRENT_CE_SCORE:
1983     case EL_PREV_CE_1:
1984     case EL_PREV_CE_2:
1985     case EL_PREV_CE_3:
1986     case EL_PREV_CE_4:
1987     case EL_PREV_CE_5:
1988     case EL_PREV_CE_6:
1989     case EL_PREV_CE_7:
1990     case EL_PREV_CE_8:
1991     case EL_NEXT_CE_1:
1992     case EL_NEXT_CE_2:
1993     case EL_NEXT_CE_3:
1994     case EL_NEXT_CE_4:
1995     case EL_NEXT_CE_5:
1996     case EL_NEXT_CE_6:
1997     case EL_NEXT_CE_7:
1998     case EL_NEXT_CE_8:
1999       // reference elements should not be used on the playfield
2000       Tile[x][y] = EL_EMPTY;
2001       break;
2002
2003     default:
2004       if (IS_CUSTOM_ELEMENT(element))
2005       {
2006         if (CAN_MOVE(element))
2007           InitMovDir(x, y);
2008
2009         if (!element_info[element].use_last_ce_value || init_game)
2010           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2011       }
2012       else if (IS_GROUP_ELEMENT(element))
2013       {
2014         Tile[x][y] = GetElementFromGroupElement(element);
2015
2016         InitField(x, y, init_game);
2017       }
2018
2019       break;
2020   }
2021
2022   if (!init_game)
2023     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2024 }
2025
2026 static void InitField_WithBug1(int x, int y, boolean init_game)
2027 {
2028   InitField(x, y, init_game);
2029
2030   // not needed to call InitMovDir() -- already done by InitField()!
2031   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2032       CAN_MOVE(Tile[x][y]))
2033     InitMovDir(x, y);
2034 }
2035
2036 static void InitField_WithBug2(int x, int y, boolean init_game)
2037 {
2038   int old_element = Tile[x][y];
2039
2040   InitField(x, y, init_game);
2041
2042   // not needed to call InitMovDir() -- already done by InitField()!
2043   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2044       CAN_MOVE(old_element) &&
2045       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2046     InitMovDir(x, y);
2047
2048   /* this case is in fact a combination of not less than three bugs:
2049      first, it calls InitMovDir() for elements that can move, although this is
2050      already done by InitField(); then, it checks the element that was at this
2051      field _before_ the call to InitField() (which can change it); lastly, it
2052      was not called for "mole with direction" elements, which were treated as
2053      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2054   */
2055 }
2056
2057 static int get_key_element_from_nr(int key_nr)
2058 {
2059   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2060                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2061                           EL_EM_KEY_1 : EL_KEY_1);
2062
2063   return key_base_element + key_nr;
2064 }
2065
2066 static int get_next_dropped_element(struct PlayerInfo *player)
2067 {
2068   return (player->inventory_size > 0 ?
2069           player->inventory_element[player->inventory_size - 1] :
2070           player->inventory_infinite_element != EL_UNDEFINED ?
2071           player->inventory_infinite_element :
2072           player->dynabombs_left > 0 ?
2073           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2074           EL_UNDEFINED);
2075 }
2076
2077 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2078 {
2079   // pos >= 0: get element from bottom of the stack;
2080   // pos <  0: get element from top of the stack
2081
2082   if (pos < 0)
2083   {
2084     int min_inventory_size = -pos;
2085     int inventory_pos = player->inventory_size - min_inventory_size;
2086     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2087
2088     return (player->inventory_size >= min_inventory_size ?
2089             player->inventory_element[inventory_pos] :
2090             player->inventory_infinite_element != EL_UNDEFINED ?
2091             player->inventory_infinite_element :
2092             player->dynabombs_left >= min_dynabombs_left ?
2093             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2094             EL_UNDEFINED);
2095   }
2096   else
2097   {
2098     int min_dynabombs_left = pos + 1;
2099     int min_inventory_size = pos + 1 - player->dynabombs_left;
2100     int inventory_pos = pos - player->dynabombs_left;
2101
2102     return (player->inventory_infinite_element != EL_UNDEFINED ?
2103             player->inventory_infinite_element :
2104             player->dynabombs_left >= min_dynabombs_left ?
2105             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2106             player->inventory_size >= min_inventory_size ?
2107             player->inventory_element[inventory_pos] :
2108             EL_UNDEFINED);
2109   }
2110 }
2111
2112 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2113 {
2114   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2115   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2116   int compare_result;
2117
2118   if (gpo1->sort_priority != gpo2->sort_priority)
2119     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2120   else
2121     compare_result = gpo1->nr - gpo2->nr;
2122
2123   return compare_result;
2124 }
2125
2126 int getPlayerInventorySize(int player_nr)
2127 {
2128   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2129     return game_em.ply[player_nr]->dynamite;
2130   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2131     return game_sp.red_disk_count;
2132   else
2133     return stored_player[player_nr].inventory_size;
2134 }
2135
2136 static void InitGameControlValues(void)
2137 {
2138   int i;
2139
2140   for (i = 0; game_panel_controls[i].nr != -1; i++)
2141   {
2142     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2143     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2144     struct TextPosInfo *pos = gpc->pos;
2145     int nr = gpc->nr;
2146     int type = gpc->type;
2147
2148     if (nr != i)
2149     {
2150       Error("'game_panel_controls' structure corrupted at %d", i);
2151
2152       Fail("this should not happen -- please debug");
2153     }
2154
2155     // force update of game controls after initialization
2156     gpc->value = gpc->last_value = -1;
2157     gpc->frame = gpc->last_frame = -1;
2158     gpc->gfx_frame = -1;
2159
2160     // determine panel value width for later calculation of alignment
2161     if (type == TYPE_INTEGER || type == TYPE_STRING)
2162     {
2163       pos->width = pos->size * getFontWidth(pos->font);
2164       pos->height = getFontHeight(pos->font);
2165     }
2166     else if (type == TYPE_ELEMENT)
2167     {
2168       pos->width = pos->size;
2169       pos->height = pos->size;
2170     }
2171
2172     // fill structure for game panel draw order
2173     gpo->nr = gpc->nr;
2174     gpo->sort_priority = pos->sort_priority;
2175   }
2176
2177   // sort game panel controls according to sort_priority and control number
2178   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2179         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2180 }
2181
2182 static void UpdatePlayfieldElementCount(void)
2183 {
2184   boolean use_element_count = FALSE;
2185   int i, j, x, y;
2186
2187   // first check if it is needed at all to calculate playfield element count
2188   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2189     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2190       use_element_count = TRUE;
2191
2192   if (!use_element_count)
2193     return;
2194
2195   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2196     element_info[i].element_count = 0;
2197
2198   SCAN_PLAYFIELD(x, y)
2199   {
2200     element_info[Tile[x][y]].element_count++;
2201   }
2202
2203   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2204     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2205       if (IS_IN_GROUP(j, i))
2206         element_info[EL_GROUP_START + i].element_count +=
2207           element_info[j].element_count;
2208 }
2209
2210 static void UpdateGameControlValues(void)
2211 {
2212   int i, k;
2213   int time = (game.LevelSolved ?
2214               game.LevelSolved_CountingTime :
2215               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2216               game_em.lev->time :
2217               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2218               game_sp.time_played :
2219               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2220               game_mm.energy_left :
2221               game.no_time_limit ? TimePlayed : TimeLeft);
2222   int score = (game.LevelSolved ?
2223                game.LevelSolved_CountingScore :
2224                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2225                game_em.lev->score :
2226                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2227                game_sp.score :
2228                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2229                game_mm.score :
2230                game.score);
2231   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2232               game_em.lev->gems_needed :
2233               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2234               game_sp.infotrons_still_needed :
2235               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2236               game_mm.kettles_still_needed :
2237               game.gems_still_needed);
2238   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2239                      game_em.lev->gems_needed > 0 :
2240                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2241                      game_sp.infotrons_still_needed > 0 :
2242                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2243                      game_mm.kettles_still_needed > 0 ||
2244                      game_mm.lights_still_needed > 0 :
2245                      game.gems_still_needed > 0 ||
2246                      game.sokoban_fields_still_needed > 0 ||
2247                      game.sokoban_objects_still_needed > 0 ||
2248                      game.lights_still_needed > 0);
2249   int health = (game.LevelSolved ?
2250                 game.LevelSolved_CountingHealth :
2251                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2252                 MM_HEALTH(game_mm.laser_overload_value) :
2253                 game.health);
2254
2255   UpdatePlayfieldElementCount();
2256
2257   // update game panel control values
2258
2259   // used instead of "level_nr" (for network games)
2260   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2261   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2262
2263   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2264   for (i = 0; i < MAX_NUM_KEYS; i++)
2265     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2266   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2267   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2268
2269   if (game.centered_player_nr == -1)
2270   {
2271     for (i = 0; i < MAX_PLAYERS; i++)
2272     {
2273       // only one player in Supaplex game engine
2274       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2275         break;
2276
2277       for (k = 0; k < MAX_NUM_KEYS; k++)
2278       {
2279         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2280         {
2281           if (game_em.ply[i]->keys & (1 << k))
2282             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2283               get_key_element_from_nr(k);
2284         }
2285         else if (stored_player[i].key[k])
2286           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2287             get_key_element_from_nr(k);
2288       }
2289
2290       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2291         getPlayerInventorySize(i);
2292
2293       if (stored_player[i].num_white_keys > 0)
2294         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2295           EL_DC_KEY_WHITE;
2296
2297       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2298         stored_player[i].num_white_keys;
2299     }
2300   }
2301   else
2302   {
2303     int player_nr = game.centered_player_nr;
2304
2305     for (k = 0; k < MAX_NUM_KEYS; k++)
2306     {
2307       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2308       {
2309         if (game_em.ply[player_nr]->keys & (1 << k))
2310           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2311             get_key_element_from_nr(k);
2312       }
2313       else if (stored_player[player_nr].key[k])
2314         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2315           get_key_element_from_nr(k);
2316     }
2317
2318     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2319       getPlayerInventorySize(player_nr);
2320
2321     if (stored_player[player_nr].num_white_keys > 0)
2322       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2323
2324     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2325       stored_player[player_nr].num_white_keys;
2326   }
2327
2328   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2329   {
2330     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2331       get_inventory_element_from_pos(local_player, i);
2332     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2333       get_inventory_element_from_pos(local_player, -i - 1);
2334   }
2335
2336   game_panel_controls[GAME_PANEL_SCORE].value = score;
2337   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2338
2339   game_panel_controls[GAME_PANEL_TIME].value = time;
2340
2341   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2342   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2343   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2344
2345   if (level.time == 0)
2346     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2347   else
2348     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2349
2350   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2351   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2352
2353   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2354
2355   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2356     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2357      EL_EMPTY);
2358   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2359     local_player->shield_normal_time_left;
2360   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2361     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2362      EL_EMPTY);
2363   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2364     local_player->shield_deadly_time_left;
2365
2366   game_panel_controls[GAME_PANEL_EXIT].value =
2367     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2368
2369   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2370     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2371   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2372     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2373      EL_EMC_MAGIC_BALL_SWITCH);
2374
2375   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2376     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2377   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2378     game.light_time_left;
2379
2380   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2381     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2382   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2383     game.timegate_time_left;
2384
2385   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2386     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2387
2388   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2389     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2390   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2391     game.lenses_time_left;
2392
2393   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2394     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2395   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2396     game.magnify_time_left;
2397
2398   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2399     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2400      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2401      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2402      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2403      EL_BALLOON_SWITCH_NONE);
2404
2405   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2406     local_player->dynabomb_count;
2407   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2408     local_player->dynabomb_size;
2409   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2410     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2411
2412   game_panel_controls[GAME_PANEL_PENGUINS].value =
2413     game.friends_still_needed;
2414
2415   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2416     game.sokoban_objects_still_needed;
2417   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2418     game.sokoban_fields_still_needed;
2419
2420   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2421     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2422
2423   for (i = 0; i < NUM_BELTS; i++)
2424   {
2425     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2426       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2427        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2428     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2429       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2430   }
2431
2432   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2433     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2434   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2435     game.magic_wall_time_left;
2436
2437   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2438     local_player->gravity;
2439
2440   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2441     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2442
2443   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2444     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2445       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2446        game.panel.element[i].id : EL_UNDEFINED);
2447
2448   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2449     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2450       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2451        element_info[game.panel.element_count[i].id].element_count : 0);
2452
2453   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2454     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2455       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2456        element_info[game.panel.ce_score[i].id].collect_score : 0);
2457
2458   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2459     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2460       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2461        element_info[game.panel.ce_score_element[i].id].collect_score :
2462        EL_UNDEFINED);
2463
2464   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2465   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2466   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2467
2468   // update game panel control frames
2469
2470   for (i = 0; game_panel_controls[i].nr != -1; i++)
2471   {
2472     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2473
2474     if (gpc->type == TYPE_ELEMENT)
2475     {
2476       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2477       {
2478         int last_anim_random_frame = gfx.anim_random_frame;
2479         int element = gpc->value;
2480         int graphic = el2panelimg(element);
2481
2482         if (gpc->value != gpc->last_value)
2483         {
2484           gpc->gfx_frame = 0;
2485           gpc->gfx_random = INIT_GFX_RANDOM();
2486         }
2487         else
2488         {
2489           gpc->gfx_frame++;
2490
2491           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2492               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2493             gpc->gfx_random = INIT_GFX_RANDOM();
2494         }
2495
2496         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2497           gfx.anim_random_frame = gpc->gfx_random;
2498
2499         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2500           gpc->gfx_frame = element_info[element].collect_score;
2501
2502         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2503                                               gpc->gfx_frame);
2504
2505         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2506           gfx.anim_random_frame = last_anim_random_frame;
2507       }
2508     }
2509     else if (gpc->type == TYPE_GRAPHIC)
2510     {
2511       if (gpc->graphic != IMG_UNDEFINED)
2512       {
2513         int last_anim_random_frame = gfx.anim_random_frame;
2514         int graphic = gpc->graphic;
2515
2516         if (gpc->value != gpc->last_value)
2517         {
2518           gpc->gfx_frame = 0;
2519           gpc->gfx_random = INIT_GFX_RANDOM();
2520         }
2521         else
2522         {
2523           gpc->gfx_frame++;
2524
2525           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2526               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2527             gpc->gfx_random = INIT_GFX_RANDOM();
2528         }
2529
2530         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2531           gfx.anim_random_frame = gpc->gfx_random;
2532
2533         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2534
2535         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2536           gfx.anim_random_frame = last_anim_random_frame;
2537       }
2538     }
2539   }
2540 }
2541
2542 static void DisplayGameControlValues(void)
2543 {
2544   boolean redraw_panel = FALSE;
2545   int i;
2546
2547   for (i = 0; game_panel_controls[i].nr != -1; i++)
2548   {
2549     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2550
2551     if (PANEL_DEACTIVATED(gpc->pos))
2552       continue;
2553
2554     if (gpc->value == gpc->last_value &&
2555         gpc->frame == gpc->last_frame)
2556       continue;
2557
2558     redraw_panel = TRUE;
2559   }
2560
2561   if (!redraw_panel)
2562     return;
2563
2564   // copy default game door content to main double buffer
2565
2566   // !!! CHECK AGAIN !!!
2567   SetPanelBackground();
2568   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2569   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2570
2571   // redraw game control buttons
2572   RedrawGameButtons();
2573
2574   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2575
2576   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2577   {
2578     int nr = game_panel_order[i].nr;
2579     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2580     struct TextPosInfo *pos = gpc->pos;
2581     int type = gpc->type;
2582     int value = gpc->value;
2583     int frame = gpc->frame;
2584     int size = pos->size;
2585     int font = pos->font;
2586     boolean draw_masked = pos->draw_masked;
2587     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2588
2589     if (PANEL_DEACTIVATED(pos))
2590       continue;
2591
2592     gpc->last_value = value;
2593     gpc->last_frame = frame;
2594
2595     if (type == TYPE_INTEGER)
2596     {
2597       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2598           nr == GAME_PANEL_TIME)
2599       {
2600         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2601
2602         if (use_dynamic_size)           // use dynamic number of digits
2603         {
2604           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2605           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2606           int size2 = size1 + 1;
2607           int font1 = pos->font;
2608           int font2 = pos->font_alt;
2609
2610           size = (value < value_change ? size1 : size2);
2611           font = (value < value_change ? font1 : font2);
2612         }
2613       }
2614
2615       // correct text size if "digits" is zero or less
2616       if (size <= 0)
2617         size = strlen(int2str(value, size));
2618
2619       // dynamically correct text alignment
2620       pos->width = size * getFontWidth(font);
2621
2622       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2623                   int2str(value, size), font, mask_mode);
2624     }
2625     else if (type == TYPE_ELEMENT)
2626     {
2627       int element, graphic;
2628       Bitmap *src_bitmap;
2629       int src_x, src_y;
2630       int width, height;
2631       int dst_x = PANEL_XPOS(pos);
2632       int dst_y = PANEL_YPOS(pos);
2633
2634       if (value != EL_UNDEFINED && value != EL_EMPTY)
2635       {
2636         element = value;
2637         graphic = el2panelimg(value);
2638
2639 #if 0
2640         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2641               element, EL_NAME(element), size);
2642 #endif
2643
2644         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2645           size = TILESIZE;
2646
2647         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2648                               &src_x, &src_y);
2649
2650         width  = graphic_info[graphic].width  * size / TILESIZE;
2651         height = graphic_info[graphic].height * size / TILESIZE;
2652
2653         if (draw_masked)
2654           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2655                            dst_x, dst_y);
2656         else
2657           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2658                      dst_x, dst_y);
2659       }
2660     }
2661     else if (type == TYPE_GRAPHIC)
2662     {
2663       int graphic        = gpc->graphic;
2664       int graphic_active = gpc->graphic_active;
2665       Bitmap *src_bitmap;
2666       int src_x, src_y;
2667       int width, height;
2668       int dst_x = PANEL_XPOS(pos);
2669       int dst_y = PANEL_YPOS(pos);
2670       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2671                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2672
2673       if (graphic != IMG_UNDEFINED && !skip)
2674       {
2675         if (pos->style == STYLE_REVERSE)
2676           value = 100 - value;
2677
2678         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2679
2680         if (pos->direction & MV_HORIZONTAL)
2681         {
2682           width  = graphic_info[graphic_active].width * value / 100;
2683           height = graphic_info[graphic_active].height;
2684
2685           if (pos->direction == MV_LEFT)
2686           {
2687             src_x += graphic_info[graphic_active].width - width;
2688             dst_x += graphic_info[graphic_active].width - width;
2689           }
2690         }
2691         else
2692         {
2693           width  = graphic_info[graphic_active].width;
2694           height = graphic_info[graphic_active].height * value / 100;
2695
2696           if (pos->direction == MV_UP)
2697           {
2698             src_y += graphic_info[graphic_active].height - height;
2699             dst_y += graphic_info[graphic_active].height - height;
2700           }
2701         }
2702
2703         if (draw_masked)
2704           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2705                            dst_x, dst_y);
2706         else
2707           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2708                      dst_x, dst_y);
2709
2710         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2711
2712         if (pos->direction & MV_HORIZONTAL)
2713         {
2714           if (pos->direction == MV_RIGHT)
2715           {
2716             src_x += width;
2717             dst_x += width;
2718           }
2719           else
2720           {
2721             dst_x = PANEL_XPOS(pos);
2722           }
2723
2724           width = graphic_info[graphic].width - width;
2725         }
2726         else
2727         {
2728           if (pos->direction == MV_DOWN)
2729           {
2730             src_y += height;
2731             dst_y += height;
2732           }
2733           else
2734           {
2735             dst_y = PANEL_YPOS(pos);
2736           }
2737
2738           height = graphic_info[graphic].height - height;
2739         }
2740
2741         if (draw_masked)
2742           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2743                            dst_x, dst_y);
2744         else
2745           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2746                      dst_x, dst_y);
2747       }
2748     }
2749     else if (type == TYPE_STRING)
2750     {
2751       boolean active = (value != 0);
2752       char *state_normal = "off";
2753       char *state_active = "on";
2754       char *state = (active ? state_active : state_normal);
2755       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2756                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2757                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2758                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2759
2760       if (nr == GAME_PANEL_GRAVITY_STATE)
2761       {
2762         int font1 = pos->font;          // (used for normal state)
2763         int font2 = pos->font_alt;      // (used for active state)
2764
2765         font = (active ? font2 : font1);
2766       }
2767
2768       if (s != NULL)
2769       {
2770         char *s_cut;
2771
2772         if (size <= 0)
2773         {
2774           // don't truncate output if "chars" is zero or less
2775           size = strlen(s);
2776
2777           // dynamically correct text alignment
2778           pos->width = size * getFontWidth(font);
2779         }
2780
2781         s_cut = getStringCopyN(s, size);
2782
2783         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2784                     s_cut, font, mask_mode);
2785
2786         free(s_cut);
2787       }
2788     }
2789
2790     redraw_mask |= REDRAW_DOOR_1;
2791   }
2792
2793   SetGameStatus(GAME_MODE_PLAYING);
2794 }
2795
2796 void UpdateAndDisplayGameControlValues(void)
2797 {
2798   if (tape.deactivate_display)
2799     return;
2800
2801   UpdateGameControlValues();
2802   DisplayGameControlValues();
2803 }
2804
2805 #if 0
2806 static void UpdateGameDoorValues(void)
2807 {
2808   UpdateGameControlValues();
2809 }
2810 #endif
2811
2812 void DrawGameDoorValues(void)
2813 {
2814   DisplayGameControlValues();
2815 }
2816
2817
2818 // ============================================================================
2819 // InitGameEngine()
2820 // ----------------------------------------------------------------------------
2821 // initialize game engine due to level / tape version number
2822 // ============================================================================
2823
2824 static void InitGameEngine(void)
2825 {
2826   int i, j, k, l, x, y;
2827
2828   // set game engine from tape file when re-playing, else from level file
2829   game.engine_version = (tape.playing ? tape.engine_version :
2830                          level.game_version);
2831
2832   // set single or multi-player game mode (needed for re-playing tapes)
2833   game.team_mode = setup.team_mode;
2834
2835   if (tape.playing)
2836   {
2837     int num_players = 0;
2838
2839     for (i = 0; i < MAX_PLAYERS; i++)
2840       if (tape.player_participates[i])
2841         num_players++;
2842
2843     // multi-player tapes contain input data for more than one player
2844     game.team_mode = (num_players > 1);
2845   }
2846
2847 #if 0
2848   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2849         level.game_version);
2850   Debug("game:init:level", "          tape.file_version   == %06d",
2851         tape.file_version);
2852   Debug("game:init:level", "          tape.game_version   == %06d",
2853         tape.game_version);
2854   Debug("game:init:level", "          tape.engine_version == %06d",
2855         tape.engine_version);
2856   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2857         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2858 #endif
2859
2860   // --------------------------------------------------------------------------
2861   // set flags for bugs and changes according to active game engine version
2862   // --------------------------------------------------------------------------
2863
2864   /*
2865     Summary of bugfix:
2866     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2867
2868     Bug was introduced in version:
2869     2.0.1
2870
2871     Bug was fixed in version:
2872     4.2.0.0
2873
2874     Description:
2875     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2876     but the property "can fall" was missing, which caused some levels to be
2877     unsolvable. This was fixed in version 4.2.0.0.
2878
2879     Affected levels/tapes:
2880     An example for a tape that was fixed by this bugfix is tape 029 from the
2881     level set "rnd_sam_bateman".
2882     The wrong behaviour will still be used for all levels or tapes that were
2883     created/recorded with it. An example for this is tape 023 from the level
2884     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2885   */
2886
2887   boolean use_amoeba_dropping_cannot_fall_bug =
2888     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2889       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2890      (tape.playing &&
2891       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2892       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2893
2894   /*
2895     Summary of bugfix/change:
2896     Fixed move speed of elements entering or leaving magic wall.
2897
2898     Fixed/changed in version:
2899     2.0.1
2900
2901     Description:
2902     Before 2.0.1, move speed of elements entering or leaving magic wall was
2903     twice as fast as it is now.
2904     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2905
2906     Affected levels/tapes:
2907     The first condition is generally needed for all levels/tapes before version
2908     2.0.1, which might use the old behaviour before it was changed; known tapes
2909     that are affected: Tape 014 from the level set "rnd_conor_mancone".
2910     The second condition is an exception from the above case and is needed for
2911     the special case of tapes recorded with game (not engine!) version 2.0.1 or
2912     above, but before it was known that this change would break tapes like the
2913     above and was fixed in 4.2.0.0, so that the changed behaviour was active
2914     although the engine version while recording maybe was before 2.0.1. There
2915     are a lot of tapes that are affected by this exception, like tape 006 from
2916     the level set "rnd_conor_mancone".
2917   */
2918
2919   boolean use_old_move_stepsize_for_magic_wall =
2920     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
2921      !(tape.playing &&
2922        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2923        tape.game_version <  VERSION_IDENT(4,2,0,0)));
2924
2925   /*
2926     Summary of bugfix/change:
2927     Fixed handling for custom elements that change when pushed by the player.
2928
2929     Fixed/changed in version:
2930     3.1.0
2931
2932     Description:
2933     Before 3.1.0, custom elements that "change when pushing" changed directly
2934     after the player started pushing them (until then handled in "DigField()").
2935     Since 3.1.0, these custom elements are not changed until the "pushing"
2936     move of the element is finished (now handled in "ContinueMoving()").
2937
2938     Affected levels/tapes:
2939     The first condition is generally needed for all levels/tapes before version
2940     3.1.0, which might use the old behaviour before it was changed; known tapes
2941     that are affected are some tapes from the level set "Walpurgis Gardens" by
2942     Jamie Cullen.
2943     The second condition is an exception from the above case and is needed for
2944     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2945     above (including some development versions of 3.1.0), but before it was
2946     known that this change would break tapes like the above and was fixed in
2947     3.1.1, so that the changed behaviour was active although the engine version
2948     while recording maybe was before 3.1.0. There is at least one tape that is
2949     affected by this exception, which is the tape for the one-level set "Bug
2950     Machine" by Juergen Bonhagen.
2951   */
2952
2953   game.use_change_when_pushing_bug =
2954     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2955      !(tape.playing &&
2956        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2957        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2958
2959   /*
2960     Summary of bugfix/change:
2961     Fixed handling for blocking the field the player leaves when moving.
2962
2963     Fixed/changed in version:
2964     3.1.1
2965
2966     Description:
2967     Before 3.1.1, when "block last field when moving" was enabled, the field
2968     the player is leaving when moving was blocked for the time of the move,
2969     and was directly unblocked afterwards. This resulted in the last field
2970     being blocked for exactly one less than the number of frames of one player
2971     move. Additionally, even when blocking was disabled, the last field was
2972     blocked for exactly one frame.
2973     Since 3.1.1, due to changes in player movement handling, the last field
2974     is not blocked at all when blocking is disabled. When blocking is enabled,
2975     the last field is blocked for exactly the number of frames of one player
2976     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2977     last field is blocked for exactly one more than the number of frames of
2978     one player move.
2979
2980     Affected levels/tapes:
2981     (!!! yet to be determined -- probably many !!!)
2982   */
2983
2984   game.use_block_last_field_bug =
2985     (game.engine_version < VERSION_IDENT(3,1,1,0));
2986
2987   /* various special flags and settings for native Emerald Mine game engine */
2988
2989   game_em.use_single_button =
2990     (game.engine_version > VERSION_IDENT(4,0,0,2));
2991
2992   game_em.use_snap_key_bug =
2993     (game.engine_version < VERSION_IDENT(4,0,1,0));
2994
2995   game_em.use_random_bug =
2996     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
2997
2998   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
2999
3000   game_em.use_old_explosions            = use_old_em_engine;
3001   game_em.use_old_android               = use_old_em_engine;
3002   game_em.use_old_push_elements         = use_old_em_engine;
3003   game_em.use_old_push_into_acid        = use_old_em_engine;
3004
3005   game_em.use_wrap_around               = !use_old_em_engine;
3006
3007   // --------------------------------------------------------------------------
3008
3009   // set maximal allowed number of custom element changes per game frame
3010   game.max_num_changes_per_frame = 1;
3011
3012   // default scan direction: scan playfield from top/left to bottom/right
3013   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3014
3015   // dynamically adjust element properties according to game engine version
3016   InitElementPropertiesEngine(game.engine_version);
3017
3018   // ---------- initialize special element properties -------------------------
3019
3020   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3021   if (use_amoeba_dropping_cannot_fall_bug)
3022     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3023
3024   // ---------- initialize player's initial move delay ------------------------
3025
3026   // dynamically adjust player properties according to level information
3027   for (i = 0; i < MAX_PLAYERS; i++)
3028     game.initial_move_delay_value[i] =
3029       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3030
3031   // dynamically adjust player properties according to game engine version
3032   for (i = 0; i < MAX_PLAYERS; i++)
3033     game.initial_move_delay[i] =
3034       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3035        game.initial_move_delay_value[i] : 0);
3036
3037   // ---------- initialize player's initial push delay ------------------------
3038
3039   // dynamically adjust player properties according to game engine version
3040   game.initial_push_delay_value =
3041     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3042
3043   // ---------- initialize changing elements ----------------------------------
3044
3045   // initialize changing elements information
3046   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3047   {
3048     struct ElementInfo *ei = &element_info[i];
3049
3050     // this pointer might have been changed in the level editor
3051     ei->change = &ei->change_page[0];
3052
3053     if (!IS_CUSTOM_ELEMENT(i))
3054     {
3055       ei->change->target_element = EL_EMPTY_SPACE;
3056       ei->change->delay_fixed = 0;
3057       ei->change->delay_random = 0;
3058       ei->change->delay_frames = 1;
3059     }
3060
3061     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3062     {
3063       ei->has_change_event[j] = FALSE;
3064
3065       ei->event_page_nr[j] = 0;
3066       ei->event_page[j] = &ei->change_page[0];
3067     }
3068   }
3069
3070   // add changing elements from pre-defined list
3071   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3072   {
3073     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3074     struct ElementInfo *ei = &element_info[ch_delay->element];
3075
3076     ei->change->target_element       = ch_delay->target_element;
3077     ei->change->delay_fixed          = ch_delay->change_delay;
3078
3079     ei->change->pre_change_function  = ch_delay->pre_change_function;
3080     ei->change->change_function      = ch_delay->change_function;
3081     ei->change->post_change_function = ch_delay->post_change_function;
3082
3083     ei->change->can_change = TRUE;
3084     ei->change->can_change_or_has_action = TRUE;
3085
3086     ei->has_change_event[CE_DELAY] = TRUE;
3087
3088     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3089     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3090   }
3091
3092   // ---------- initialize internal run-time variables ------------------------
3093
3094   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3095   {
3096     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3097
3098     for (j = 0; j < ei->num_change_pages; j++)
3099     {
3100       ei->change_page[j].can_change_or_has_action =
3101         (ei->change_page[j].can_change |
3102          ei->change_page[j].has_action);
3103     }
3104   }
3105
3106   // add change events from custom element configuration
3107   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3108   {
3109     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3110
3111     for (j = 0; j < ei->num_change_pages; j++)
3112     {
3113       if (!ei->change_page[j].can_change_or_has_action)
3114         continue;
3115
3116       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3117       {
3118         // only add event page for the first page found with this event
3119         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3120         {
3121           ei->has_change_event[k] = TRUE;
3122
3123           ei->event_page_nr[k] = j;
3124           ei->event_page[k] = &ei->change_page[j];
3125         }
3126       }
3127     }
3128   }
3129
3130   // ---------- initialize reference elements in change conditions ------------
3131
3132   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3133   {
3134     int element = EL_CUSTOM_START + i;
3135     struct ElementInfo *ei = &element_info[element];
3136
3137     for (j = 0; j < ei->num_change_pages; j++)
3138     {
3139       int trigger_element = ei->change_page[j].initial_trigger_element;
3140
3141       if (trigger_element >= EL_PREV_CE_8 &&
3142           trigger_element <= EL_NEXT_CE_8)
3143         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3144
3145       ei->change_page[j].trigger_element = trigger_element;
3146     }
3147   }
3148
3149   // ---------- initialize run-time trigger player and element ----------------
3150
3151   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3152   {
3153     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3154
3155     for (j = 0; j < ei->num_change_pages; j++)
3156     {
3157       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3158       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3159       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3160       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3161       ei->change_page[j].actual_trigger_ce_value = 0;
3162       ei->change_page[j].actual_trigger_ce_score = 0;
3163     }
3164   }
3165
3166   // ---------- initialize trigger events -------------------------------------
3167
3168   // initialize trigger events information
3169   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3170     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3171       trigger_events[i][j] = FALSE;
3172
3173   // add trigger events from element change event properties
3174   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3175   {
3176     struct ElementInfo *ei = &element_info[i];
3177
3178     for (j = 0; j < ei->num_change_pages; j++)
3179     {
3180       if (!ei->change_page[j].can_change_or_has_action)
3181         continue;
3182
3183       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3184       {
3185         int trigger_element = ei->change_page[j].trigger_element;
3186
3187         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3188         {
3189           if (ei->change_page[j].has_event[k])
3190           {
3191             if (IS_GROUP_ELEMENT(trigger_element))
3192             {
3193               struct ElementGroupInfo *group =
3194                 element_info[trigger_element].group;
3195
3196               for (l = 0; l < group->num_elements_resolved; l++)
3197                 trigger_events[group->element_resolved[l]][k] = TRUE;
3198             }
3199             else if (trigger_element == EL_ANY_ELEMENT)
3200               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3201                 trigger_events[l][k] = TRUE;
3202             else
3203               trigger_events[trigger_element][k] = TRUE;
3204           }
3205         }
3206       }
3207     }
3208   }
3209
3210   // ---------- initialize push delay -----------------------------------------
3211
3212   // initialize push delay values to default
3213   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3214   {
3215     if (!IS_CUSTOM_ELEMENT(i))
3216     {
3217       // set default push delay values (corrected since version 3.0.7-1)
3218       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3219       {
3220         element_info[i].push_delay_fixed = 2;
3221         element_info[i].push_delay_random = 8;
3222       }
3223       else
3224       {
3225         element_info[i].push_delay_fixed = 8;
3226         element_info[i].push_delay_random = 8;
3227       }
3228     }
3229   }
3230
3231   // set push delay value for certain elements from pre-defined list
3232   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3233   {
3234     int e = push_delay_list[i].element;
3235
3236     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3237     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3238   }
3239
3240   // set push delay value for Supaplex elements for newer engine versions
3241   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3242   {
3243     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3244     {
3245       if (IS_SP_ELEMENT(i))
3246       {
3247         // set SP push delay to just enough to push under a falling zonk
3248         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3249
3250         element_info[i].push_delay_fixed  = delay;
3251         element_info[i].push_delay_random = 0;
3252       }
3253     }
3254   }
3255
3256   // ---------- initialize move stepsize --------------------------------------
3257
3258   // initialize move stepsize values to default
3259   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3260     if (!IS_CUSTOM_ELEMENT(i))
3261       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3262
3263   // set move stepsize value for certain elements from pre-defined list
3264   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3265   {
3266     int e = move_stepsize_list[i].element;
3267
3268     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3269
3270     // set move stepsize value for certain elements for older engine versions
3271     if (use_old_move_stepsize_for_magic_wall)
3272     {
3273       if (e == EL_MAGIC_WALL_FILLING ||
3274           e == EL_MAGIC_WALL_EMPTYING ||
3275           e == EL_BD_MAGIC_WALL_FILLING ||
3276           e == EL_BD_MAGIC_WALL_EMPTYING)
3277         element_info[e].move_stepsize *= 2;
3278     }
3279   }
3280
3281   // ---------- initialize collect score --------------------------------------
3282
3283   // initialize collect score values for custom elements from initial value
3284   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3285     if (IS_CUSTOM_ELEMENT(i))
3286       element_info[i].collect_score = element_info[i].collect_score_initial;
3287
3288   // ---------- initialize collect count --------------------------------------
3289
3290   // initialize collect count values for non-custom elements
3291   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3292     if (!IS_CUSTOM_ELEMENT(i))
3293       element_info[i].collect_count_initial = 0;
3294
3295   // add collect count values for all elements from pre-defined list
3296   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3297     element_info[collect_count_list[i].element].collect_count_initial =
3298       collect_count_list[i].count;
3299
3300   // ---------- initialize access direction -----------------------------------
3301
3302   // initialize access direction values to default (access from every side)
3303   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3304     if (!IS_CUSTOM_ELEMENT(i))
3305       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3306
3307   // set access direction value for certain elements from pre-defined list
3308   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3309     element_info[access_direction_list[i].element].access_direction =
3310       access_direction_list[i].direction;
3311
3312   // ---------- initialize explosion content ----------------------------------
3313   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3314   {
3315     if (IS_CUSTOM_ELEMENT(i))
3316       continue;
3317
3318     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3319     {
3320       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3321
3322       element_info[i].content.e[x][y] =
3323         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3324          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3325          i == EL_PLAYER_3 ? EL_EMERALD :
3326          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3327          i == EL_MOLE ? EL_EMERALD_RED :
3328          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3329          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3330          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3331          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3332          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3333          i == EL_WALL_EMERALD ? EL_EMERALD :
3334          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3335          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3336          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3337          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3338          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3339          i == EL_WALL_PEARL ? EL_PEARL :
3340          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3341          EL_EMPTY);
3342     }
3343   }
3344
3345   // ---------- initialize recursion detection --------------------------------
3346   recursion_loop_depth = 0;
3347   recursion_loop_detected = FALSE;
3348   recursion_loop_element = EL_UNDEFINED;
3349
3350   // ---------- initialize graphics engine ------------------------------------
3351   game.scroll_delay_value =
3352     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3353      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3354      !setup.forced_scroll_delay           ? 0 :
3355      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3356   game.scroll_delay_value =
3357     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3358
3359   // ---------- initialize game engine snapshots ------------------------------
3360   for (i = 0; i < MAX_PLAYERS; i++)
3361     game.snapshot.last_action[i] = 0;
3362   game.snapshot.changed_action = FALSE;
3363   game.snapshot.collected_item = FALSE;
3364   game.snapshot.mode =
3365     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3366      SNAPSHOT_MODE_EVERY_STEP :
3367      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3368      SNAPSHOT_MODE_EVERY_MOVE :
3369      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3370      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3371   game.snapshot.save_snapshot = FALSE;
3372
3373   // ---------- initialize level time for Supaplex engine ---------------------
3374   // Supaplex levels with time limit currently unsupported -- should be added
3375   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3376     level.time = 0;
3377
3378   // ---------- initialize flags for handling game actions --------------------
3379
3380   // set flags for game actions to default values
3381   game.use_key_actions = TRUE;
3382   game.use_mouse_actions = FALSE;
3383
3384   // when using Mirror Magic game engine, handle mouse events only
3385   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3386   {
3387     game.use_key_actions = FALSE;
3388     game.use_mouse_actions = TRUE;
3389   }
3390
3391   // check for custom elements with mouse click events
3392   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3393   {
3394     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3395     {
3396       int element = EL_CUSTOM_START + i;
3397
3398       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3399           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3400           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3401           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3402         game.use_mouse_actions = TRUE;
3403     }
3404   }
3405 }
3406
3407 static int get_num_special_action(int element, int action_first,
3408                                   int action_last)
3409 {
3410   int num_special_action = 0;
3411   int i, j;
3412
3413   for (i = action_first; i <= action_last; i++)
3414   {
3415     boolean found = FALSE;
3416
3417     for (j = 0; j < NUM_DIRECTIONS; j++)
3418       if (el_act_dir2img(element, i, j) !=
3419           el_act_dir2img(element, ACTION_DEFAULT, j))
3420         found = TRUE;
3421
3422     if (found)
3423       num_special_action++;
3424     else
3425       break;
3426   }
3427
3428   return num_special_action;
3429 }
3430
3431
3432 // ============================================================================
3433 // InitGame()
3434 // ----------------------------------------------------------------------------
3435 // initialize and start new game
3436 // ============================================================================
3437
3438 #if DEBUG_INIT_PLAYER
3439 static void DebugPrintPlayerStatus(char *message)
3440 {
3441   int i;
3442
3443   if (!options.debug)
3444     return;
3445
3446   Debug("game:init:player", "%s:", message);
3447
3448   for (i = 0; i < MAX_PLAYERS; i++)
3449   {
3450     struct PlayerInfo *player = &stored_player[i];
3451
3452     Debug("game:init:player",
3453           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3454           i + 1,
3455           player->present,
3456           player->connected,
3457           player->connected_locally,
3458           player->connected_network,
3459           player->active,
3460           (local_player == player ? " (local player)" : ""));
3461   }
3462 }
3463 #endif
3464
3465 void InitGame(void)
3466 {
3467   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3468   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3469   int fade_mask = REDRAW_FIELD;
3470
3471   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3472   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3473   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3474   int initial_move_dir = MV_DOWN;
3475   int i, j, x, y;
3476
3477   // required here to update video display before fading (FIX THIS)
3478   DrawMaskedBorder(REDRAW_DOOR_2);
3479
3480   if (!game.restart_level)
3481     CloseDoor(DOOR_CLOSE_1);
3482
3483   SetGameStatus(GAME_MODE_PLAYING);
3484
3485   if (level_editor_test_game)
3486     FadeSkipNextFadeOut();
3487   else
3488     FadeSetEnterScreen();
3489
3490   if (CheckFadeAll())
3491     fade_mask = REDRAW_ALL;
3492
3493   FadeLevelSoundsAndMusic();
3494
3495   ExpireSoundLoops(TRUE);
3496
3497   FadeOut(fade_mask);
3498
3499   if (level_editor_test_game)
3500     FadeSkipNextFadeIn();
3501
3502   // needed if different viewport properties defined for playing
3503   ChangeViewportPropertiesIfNeeded();
3504
3505   ClearField();
3506
3507   DrawCompleteVideoDisplay();
3508
3509   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3510
3511   InitGameEngine();
3512   InitGameControlValues();
3513
3514   // initialize tape actions from game when recording tape
3515   if (tape.recording)
3516   {
3517     tape.use_key_actions   = game.use_key_actions;
3518     tape.use_mouse_actions = game.use_mouse_actions;
3519   }
3520
3521   // don't play tapes over network
3522   network_playing = (network.enabled && !tape.playing);
3523
3524   for (i = 0; i < MAX_PLAYERS; i++)
3525   {
3526     struct PlayerInfo *player = &stored_player[i];
3527
3528     player->index_nr = i;
3529     player->index_bit = (1 << i);
3530     player->element_nr = EL_PLAYER_1 + i;
3531
3532     player->present = FALSE;
3533     player->active = FALSE;
3534     player->mapped = FALSE;
3535
3536     player->killed = FALSE;
3537     player->reanimated = FALSE;
3538     player->buried = FALSE;
3539
3540     player->action = 0;
3541     player->effective_action = 0;
3542     player->programmed_action = 0;
3543     player->snap_action = 0;
3544
3545     player->mouse_action.lx = 0;
3546     player->mouse_action.ly = 0;
3547     player->mouse_action.button = 0;
3548     player->mouse_action.button_hint = 0;
3549
3550     player->effective_mouse_action.lx = 0;
3551     player->effective_mouse_action.ly = 0;
3552     player->effective_mouse_action.button = 0;
3553     player->effective_mouse_action.button_hint = 0;
3554
3555     for (j = 0; j < MAX_NUM_KEYS; j++)
3556       player->key[j] = FALSE;
3557
3558     player->num_white_keys = 0;
3559
3560     player->dynabomb_count = 0;
3561     player->dynabomb_size = 1;
3562     player->dynabombs_left = 0;
3563     player->dynabomb_xl = FALSE;
3564
3565     player->MovDir = initial_move_dir;
3566     player->MovPos = 0;
3567     player->GfxPos = 0;
3568     player->GfxDir = initial_move_dir;
3569     player->GfxAction = ACTION_DEFAULT;
3570     player->Frame = 0;
3571     player->StepFrame = 0;
3572
3573     player->initial_element = player->element_nr;
3574     player->artwork_element =
3575       (level.use_artwork_element[i] ? level.artwork_element[i] :
3576        player->element_nr);
3577     player->use_murphy = FALSE;
3578
3579     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3580     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3581
3582     player->gravity = level.initial_player_gravity[i];
3583
3584     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3585
3586     player->actual_frame_counter = 0;
3587
3588     player->step_counter = 0;
3589
3590     player->last_move_dir = initial_move_dir;
3591
3592     player->is_active = FALSE;
3593
3594     player->is_waiting = FALSE;
3595     player->is_moving = FALSE;
3596     player->is_auto_moving = FALSE;
3597     player->is_digging = FALSE;
3598     player->is_snapping = FALSE;
3599     player->is_collecting = FALSE;
3600     player->is_pushing = FALSE;
3601     player->is_switching = FALSE;
3602     player->is_dropping = FALSE;
3603     player->is_dropping_pressed = FALSE;
3604
3605     player->is_bored = FALSE;
3606     player->is_sleeping = FALSE;
3607
3608     player->was_waiting = TRUE;
3609     player->was_moving = FALSE;
3610     player->was_snapping = FALSE;
3611     player->was_dropping = FALSE;
3612
3613     player->force_dropping = FALSE;
3614
3615     player->frame_counter_bored = -1;
3616     player->frame_counter_sleeping = -1;
3617
3618     player->anim_delay_counter = 0;
3619     player->post_delay_counter = 0;
3620
3621     player->dir_waiting = initial_move_dir;
3622     player->action_waiting = ACTION_DEFAULT;
3623     player->last_action_waiting = ACTION_DEFAULT;
3624     player->special_action_bored = ACTION_DEFAULT;
3625     player->special_action_sleeping = ACTION_DEFAULT;
3626
3627     player->switch_x = -1;
3628     player->switch_y = -1;
3629
3630     player->drop_x = -1;
3631     player->drop_y = -1;
3632
3633     player->show_envelope = 0;
3634
3635     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3636
3637     player->push_delay       = -1;      // initialized when pushing starts
3638     player->push_delay_value = game.initial_push_delay_value;
3639
3640     player->drop_delay = 0;
3641     player->drop_pressed_delay = 0;
3642
3643     player->last_jx = -1;
3644     player->last_jy = -1;
3645     player->jx = -1;
3646     player->jy = -1;
3647
3648     player->shield_normal_time_left = 0;
3649     player->shield_deadly_time_left = 0;
3650
3651     player->inventory_infinite_element = EL_UNDEFINED;
3652     player->inventory_size = 0;
3653
3654     if (level.use_initial_inventory[i])
3655     {
3656       for (j = 0; j < level.initial_inventory_size[i]; j++)
3657       {
3658         int element = level.initial_inventory_content[i][j];
3659         int collect_count = element_info[element].collect_count_initial;
3660         int k;
3661
3662         if (!IS_CUSTOM_ELEMENT(element))
3663           collect_count = 1;
3664
3665         if (collect_count == 0)
3666           player->inventory_infinite_element = element;
3667         else
3668           for (k = 0; k < collect_count; k++)
3669             if (player->inventory_size < MAX_INVENTORY_SIZE)
3670               player->inventory_element[player->inventory_size++] = element;
3671       }
3672     }
3673
3674     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3675     SnapField(player, 0, 0);
3676
3677     map_player_action[i] = i;
3678   }
3679
3680   network_player_action_received = FALSE;
3681
3682   // initial null action
3683   if (network_playing)
3684     SendToServer_MovePlayer(MV_NONE);
3685
3686   FrameCounter = 0;
3687   TimeFrames = 0;
3688   TimePlayed = 0;
3689   TimeLeft = level.time;
3690   TapeTime = 0;
3691
3692   ScreenMovDir = MV_NONE;
3693   ScreenMovPos = 0;
3694   ScreenGfxPos = 0;
3695
3696   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3697
3698   game.robot_wheel_x = -1;
3699   game.robot_wheel_y = -1;
3700
3701   game.exit_x = -1;
3702   game.exit_y = -1;
3703
3704   game.all_players_gone = FALSE;
3705
3706   game.LevelSolved = FALSE;
3707   game.GameOver = FALSE;
3708
3709   game.GamePlayed = !tape.playing;
3710
3711   game.LevelSolved_GameWon = FALSE;
3712   game.LevelSolved_GameEnd = FALSE;
3713   game.LevelSolved_SaveTape = FALSE;
3714   game.LevelSolved_SaveScore = FALSE;
3715
3716   game.LevelSolved_CountingTime = 0;
3717   game.LevelSolved_CountingScore = 0;
3718   game.LevelSolved_CountingHealth = 0;
3719
3720   game.panel.active = TRUE;
3721
3722   game.no_time_limit = (level.time == 0);
3723
3724   game.yamyam_content_nr = 0;
3725   game.robot_wheel_active = FALSE;
3726   game.magic_wall_active = FALSE;
3727   game.magic_wall_time_left = 0;
3728   game.light_time_left = 0;
3729   game.timegate_time_left = 0;
3730   game.switchgate_pos = 0;
3731   game.wind_direction = level.wind_direction_initial;
3732
3733   game.score = 0;
3734   game.score_final = 0;
3735
3736   game.health = MAX_HEALTH;
3737   game.health_final = MAX_HEALTH;
3738
3739   game.gems_still_needed = level.gems_needed;
3740   game.sokoban_fields_still_needed = 0;
3741   game.sokoban_objects_still_needed = 0;
3742   game.lights_still_needed = 0;
3743   game.players_still_needed = 0;
3744   game.friends_still_needed = 0;
3745
3746   game.lenses_time_left = 0;
3747   game.magnify_time_left = 0;
3748
3749   game.ball_active = level.ball_active_initial;
3750   game.ball_content_nr = 0;
3751
3752   game.explosions_delayed = TRUE;
3753
3754   game.envelope_active = FALSE;
3755
3756   for (i = 0; i < NUM_BELTS; i++)
3757   {
3758     game.belt_dir[i] = MV_NONE;
3759     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3760   }
3761
3762   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3763     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3764
3765 #if DEBUG_INIT_PLAYER
3766   DebugPrintPlayerStatus("Player status at level initialization");
3767 #endif
3768
3769   SCAN_PLAYFIELD(x, y)
3770   {
3771     Tile[x][y] = Last[x][y] = level.field[x][y];
3772     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3773     ChangeDelay[x][y] = 0;
3774     ChangePage[x][y] = -1;
3775     CustomValue[x][y] = 0;              // initialized in InitField()
3776     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3777     AmoebaNr[x][y] = 0;
3778     WasJustMoving[x][y] = 0;
3779     WasJustFalling[x][y] = 0;
3780     CheckCollision[x][y] = 0;
3781     CheckImpact[x][y] = 0;
3782     Stop[x][y] = FALSE;
3783     Pushed[x][y] = FALSE;
3784
3785     ChangeCount[x][y] = 0;
3786     ChangeEvent[x][y] = -1;
3787
3788     ExplodePhase[x][y] = 0;
3789     ExplodeDelay[x][y] = 0;
3790     ExplodeField[x][y] = EX_TYPE_NONE;
3791
3792     RunnerVisit[x][y] = 0;
3793     PlayerVisit[x][y] = 0;
3794
3795     GfxFrame[x][y] = 0;
3796     GfxRandom[x][y] = INIT_GFX_RANDOM();
3797     GfxElement[x][y] = EL_UNDEFINED;
3798     GfxAction[x][y] = ACTION_DEFAULT;
3799     GfxDir[x][y] = MV_NONE;
3800     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3801   }
3802
3803   SCAN_PLAYFIELD(x, y)
3804   {
3805     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3806       emulate_bd = FALSE;
3807     if (emulate_sb && !IS_SB_ELEMENT(Tile[x][y]))
3808       emulate_sb = FALSE;
3809     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3810       emulate_sp = FALSE;
3811
3812     InitField(x, y, TRUE);
3813
3814     ResetGfxAnimation(x, y);
3815   }
3816
3817   InitBeltMovement();
3818
3819   for (i = 0; i < MAX_PLAYERS; i++)
3820   {
3821     struct PlayerInfo *player = &stored_player[i];
3822
3823     // set number of special actions for bored and sleeping animation
3824     player->num_special_action_bored =
3825       get_num_special_action(player->artwork_element,
3826                              ACTION_BORING_1, ACTION_BORING_LAST);
3827     player->num_special_action_sleeping =
3828       get_num_special_action(player->artwork_element,
3829                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3830   }
3831
3832   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3833                     emulate_sb ? EMU_SOKOBAN :
3834                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3835
3836   // initialize type of slippery elements
3837   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3838   {
3839     if (!IS_CUSTOM_ELEMENT(i))
3840     {
3841       // default: elements slip down either to the left or right randomly
3842       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3843
3844       // SP style elements prefer to slip down on the left side
3845       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3846         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3847
3848       // BD style elements prefer to slip down on the left side
3849       if (game.emulation == EMU_BOULDERDASH)
3850         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3851     }
3852   }
3853
3854   // initialize explosion and ignition delay
3855   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3856   {
3857     if (!IS_CUSTOM_ELEMENT(i))
3858     {
3859       int num_phase = 8;
3860       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3861                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3862                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3863       int last_phase = (num_phase + 1) * delay;
3864       int half_phase = (num_phase / 2) * delay;
3865
3866       element_info[i].explosion_delay = last_phase - 1;
3867       element_info[i].ignition_delay = half_phase;
3868
3869       if (i == EL_BLACK_ORB)
3870         element_info[i].ignition_delay = 1;
3871     }
3872   }
3873
3874   // correct non-moving belts to start moving left
3875   for (i = 0; i < NUM_BELTS; i++)
3876     if (game.belt_dir[i] == MV_NONE)
3877       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3878
3879 #if USE_NEW_PLAYER_ASSIGNMENTS
3880   // use preferred player also in local single-player mode
3881   if (!network.enabled && !game.team_mode)
3882   {
3883     int new_index_nr = setup.network_player_nr;
3884
3885     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3886     {
3887       for (i = 0; i < MAX_PLAYERS; i++)
3888         stored_player[i].connected_locally = FALSE;
3889
3890       stored_player[new_index_nr].connected_locally = TRUE;
3891     }
3892   }
3893
3894   for (i = 0; i < MAX_PLAYERS; i++)
3895   {
3896     stored_player[i].connected = FALSE;
3897
3898     // in network game mode, the local player might not be the first player
3899     if (stored_player[i].connected_locally)
3900       local_player = &stored_player[i];
3901   }
3902
3903   if (!network.enabled)
3904     local_player->connected = TRUE;
3905
3906   if (tape.playing)
3907   {
3908     for (i = 0; i < MAX_PLAYERS; i++)
3909       stored_player[i].connected = tape.player_participates[i];
3910   }
3911   else if (network.enabled)
3912   {
3913     // add team mode players connected over the network (needed for correct
3914     // assignment of player figures from level to locally playing players)
3915
3916     for (i = 0; i < MAX_PLAYERS; i++)
3917       if (stored_player[i].connected_network)
3918         stored_player[i].connected = TRUE;
3919   }
3920   else if (game.team_mode)
3921   {
3922     // try to guess locally connected team mode players (needed for correct
3923     // assignment of player figures from level to locally playing players)
3924
3925     for (i = 0; i < MAX_PLAYERS; i++)
3926       if (setup.input[i].use_joystick ||
3927           setup.input[i].key.left != KSYM_UNDEFINED)
3928         stored_player[i].connected = TRUE;
3929   }
3930
3931 #if DEBUG_INIT_PLAYER
3932   DebugPrintPlayerStatus("Player status after level initialization");
3933 #endif
3934
3935 #if DEBUG_INIT_PLAYER
3936   Debug("game:init:player", "Reassigning players ...");
3937 #endif
3938
3939   // check if any connected player was not found in playfield
3940   for (i = 0; i < MAX_PLAYERS; i++)
3941   {
3942     struct PlayerInfo *player = &stored_player[i];
3943
3944     if (player->connected && !player->present)
3945     {
3946       struct PlayerInfo *field_player = NULL;
3947
3948 #if DEBUG_INIT_PLAYER
3949       Debug("game:init:player",
3950             "- looking for field player for player %d ...", i + 1);
3951 #endif
3952
3953       // assign first free player found that is present in the playfield
3954
3955       // first try: look for unmapped playfield player that is not connected
3956       for (j = 0; j < MAX_PLAYERS; j++)
3957         if (field_player == NULL &&
3958             stored_player[j].present &&
3959             !stored_player[j].mapped &&
3960             !stored_player[j].connected)
3961           field_player = &stored_player[j];
3962
3963       // second try: look for *any* unmapped playfield player
3964       for (j = 0; j < MAX_PLAYERS; j++)
3965         if (field_player == NULL &&
3966             stored_player[j].present &&
3967             !stored_player[j].mapped)
3968           field_player = &stored_player[j];
3969
3970       if (field_player != NULL)
3971       {
3972         int jx = field_player->jx, jy = field_player->jy;
3973
3974 #if DEBUG_INIT_PLAYER
3975         Debug("game:init:player", "- found player %d",
3976               field_player->index_nr + 1);
3977 #endif
3978
3979         player->present = FALSE;
3980         player->active = FALSE;
3981
3982         field_player->present = TRUE;
3983         field_player->active = TRUE;
3984
3985         /*
3986         player->initial_element = field_player->initial_element;
3987         player->artwork_element = field_player->artwork_element;
3988
3989         player->block_last_field       = field_player->block_last_field;
3990         player->block_delay_adjustment = field_player->block_delay_adjustment;
3991         */
3992
3993         StorePlayer[jx][jy] = field_player->element_nr;
3994
3995         field_player->jx = field_player->last_jx = jx;
3996         field_player->jy = field_player->last_jy = jy;
3997
3998         if (local_player == player)
3999           local_player = field_player;
4000
4001         map_player_action[field_player->index_nr] = i;
4002
4003         field_player->mapped = TRUE;
4004
4005 #if DEBUG_INIT_PLAYER
4006         Debug("game:init:player", "- map_player_action[%d] == %d",
4007               field_player->index_nr + 1, i + 1);
4008 #endif
4009       }
4010     }
4011
4012     if (player->connected && player->present)
4013       player->mapped = TRUE;
4014   }
4015
4016 #if DEBUG_INIT_PLAYER
4017   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4018 #endif
4019
4020 #else
4021
4022   // check if any connected player was not found in playfield
4023   for (i = 0; i < MAX_PLAYERS; i++)
4024   {
4025     struct PlayerInfo *player = &stored_player[i];
4026
4027     if (player->connected && !player->present)
4028     {
4029       for (j = 0; j < MAX_PLAYERS; j++)
4030       {
4031         struct PlayerInfo *field_player = &stored_player[j];
4032         int jx = field_player->jx, jy = field_player->jy;
4033
4034         // assign first free player found that is present in the playfield
4035         if (field_player->present && !field_player->connected)
4036         {
4037           player->present = TRUE;
4038           player->active = TRUE;
4039
4040           field_player->present = FALSE;
4041           field_player->active = FALSE;
4042
4043           player->initial_element = field_player->initial_element;
4044           player->artwork_element = field_player->artwork_element;
4045
4046           player->block_last_field       = field_player->block_last_field;
4047           player->block_delay_adjustment = field_player->block_delay_adjustment;
4048
4049           StorePlayer[jx][jy] = player->element_nr;
4050
4051           player->jx = player->last_jx = jx;
4052           player->jy = player->last_jy = jy;
4053
4054           break;
4055         }
4056       }
4057     }
4058   }
4059 #endif
4060
4061 #if 0
4062   Debug("game:init:player", "local_player->present == %d",
4063         local_player->present);
4064 #endif
4065
4066   // set focus to local player for network games, else to all players
4067   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4068   game.centered_player_nr_next = game.centered_player_nr;
4069   game.set_centered_player = FALSE;
4070   game.set_centered_player_wrap = FALSE;
4071
4072   if (network_playing && tape.recording)
4073   {
4074     // store client dependent player focus when recording network games
4075     tape.centered_player_nr_next = game.centered_player_nr_next;
4076     tape.set_centered_player = TRUE;
4077   }
4078
4079   if (tape.playing)
4080   {
4081     // when playing a tape, eliminate all players who do not participate
4082
4083 #if USE_NEW_PLAYER_ASSIGNMENTS
4084
4085     if (!game.team_mode)
4086     {
4087       for (i = 0; i < MAX_PLAYERS; i++)
4088       {
4089         if (stored_player[i].active &&
4090             !tape.player_participates[map_player_action[i]])
4091         {
4092           struct PlayerInfo *player = &stored_player[i];
4093           int jx = player->jx, jy = player->jy;
4094
4095 #if DEBUG_INIT_PLAYER
4096           Debug("game:init:player", "Removing player %d at (%d, %d)",
4097                 i + 1, jx, jy);
4098 #endif
4099
4100           player->active = FALSE;
4101           StorePlayer[jx][jy] = 0;
4102           Tile[jx][jy] = EL_EMPTY;
4103         }
4104       }
4105     }
4106
4107 #else
4108
4109     for (i = 0; i < MAX_PLAYERS; i++)
4110     {
4111       if (stored_player[i].active &&
4112           !tape.player_participates[i])
4113       {
4114         struct PlayerInfo *player = &stored_player[i];
4115         int jx = player->jx, jy = player->jy;
4116
4117         player->active = FALSE;
4118         StorePlayer[jx][jy] = 0;
4119         Tile[jx][jy] = EL_EMPTY;
4120       }
4121     }
4122 #endif
4123   }
4124   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4125   {
4126     // when in single player mode, eliminate all but the local player
4127
4128     for (i = 0; i < MAX_PLAYERS; i++)
4129     {
4130       struct PlayerInfo *player = &stored_player[i];
4131
4132       if (player->active && player != local_player)
4133       {
4134         int jx = player->jx, jy = player->jy;
4135
4136         player->active = FALSE;
4137         player->present = FALSE;
4138
4139         StorePlayer[jx][jy] = 0;
4140         Tile[jx][jy] = EL_EMPTY;
4141       }
4142     }
4143   }
4144
4145   for (i = 0; i < MAX_PLAYERS; i++)
4146     if (stored_player[i].active)
4147       game.players_still_needed++;
4148
4149   if (level.solved_by_one_player)
4150     game.players_still_needed = 1;
4151
4152   // when recording the game, store which players take part in the game
4153   if (tape.recording)
4154   {
4155 #if USE_NEW_PLAYER_ASSIGNMENTS
4156     for (i = 0; i < MAX_PLAYERS; i++)
4157       if (stored_player[i].connected)
4158         tape.player_participates[i] = TRUE;
4159 #else
4160     for (i = 0; i < MAX_PLAYERS; i++)
4161       if (stored_player[i].active)
4162         tape.player_participates[i] = TRUE;
4163 #endif
4164   }
4165
4166 #if DEBUG_INIT_PLAYER
4167   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4168 #endif
4169
4170   if (BorderElement == EL_EMPTY)
4171   {
4172     SBX_Left = 0;
4173     SBX_Right = lev_fieldx - SCR_FIELDX;
4174     SBY_Upper = 0;
4175     SBY_Lower = lev_fieldy - SCR_FIELDY;
4176   }
4177   else
4178   {
4179     SBX_Left = -1;
4180     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4181     SBY_Upper = -1;
4182     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4183   }
4184
4185   if (full_lev_fieldx <= SCR_FIELDX)
4186     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4187   if (full_lev_fieldy <= SCR_FIELDY)
4188     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4189
4190   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4191     SBX_Left--;
4192   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4193     SBY_Upper--;
4194
4195   // if local player not found, look for custom element that might create
4196   // the player (make some assumptions about the right custom element)
4197   if (!local_player->present)
4198   {
4199     int start_x = 0, start_y = 0;
4200     int found_rating = 0;
4201     int found_element = EL_UNDEFINED;
4202     int player_nr = local_player->index_nr;
4203
4204     SCAN_PLAYFIELD(x, y)
4205     {
4206       int element = Tile[x][y];
4207       int content;
4208       int xx, yy;
4209       boolean is_player;
4210
4211       if (level.use_start_element[player_nr] &&
4212           level.start_element[player_nr] == element &&
4213           found_rating < 4)
4214       {
4215         start_x = x;
4216         start_y = y;
4217
4218         found_rating = 4;
4219         found_element = element;
4220       }
4221
4222       if (!IS_CUSTOM_ELEMENT(element))
4223         continue;
4224
4225       if (CAN_CHANGE(element))
4226       {
4227         for (i = 0; i < element_info[element].num_change_pages; i++)
4228         {
4229           // check for player created from custom element as single target
4230           content = element_info[element].change_page[i].target_element;
4231           is_player = ELEM_IS_PLAYER(content);
4232
4233           if (is_player && (found_rating < 3 ||
4234                             (found_rating == 3 && element < found_element)))
4235           {
4236             start_x = x;
4237             start_y = y;
4238
4239             found_rating = 3;
4240             found_element = element;
4241           }
4242         }
4243       }
4244
4245       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4246       {
4247         // check for player created from custom element as explosion content
4248         content = element_info[element].content.e[xx][yy];
4249         is_player = ELEM_IS_PLAYER(content);
4250
4251         if (is_player && (found_rating < 2 ||
4252                           (found_rating == 2 && element < found_element)))
4253         {
4254           start_x = x + xx - 1;
4255           start_y = y + yy - 1;
4256
4257           found_rating = 2;
4258           found_element = element;
4259         }
4260
4261         if (!CAN_CHANGE(element))
4262           continue;
4263
4264         for (i = 0; i < element_info[element].num_change_pages; i++)
4265         {
4266           // check for player created from custom element as extended target
4267           content =
4268             element_info[element].change_page[i].target_content.e[xx][yy];
4269
4270           is_player = ELEM_IS_PLAYER(content);
4271
4272           if (is_player && (found_rating < 1 ||
4273                             (found_rating == 1 && element < found_element)))
4274           {
4275             start_x = x + xx - 1;
4276             start_y = y + yy - 1;
4277
4278             found_rating = 1;
4279             found_element = element;
4280           }
4281         }
4282       }
4283     }
4284
4285     scroll_x = SCROLL_POSITION_X(start_x);
4286     scroll_y = SCROLL_POSITION_Y(start_y);
4287   }
4288   else
4289   {
4290     scroll_x = SCROLL_POSITION_X(local_player->jx);
4291     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4292   }
4293
4294   // !!! FIX THIS (START) !!!
4295   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4296   {
4297     InitGameEngine_EM();
4298   }
4299   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4300   {
4301     InitGameEngine_SP();
4302   }
4303   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4304   {
4305     InitGameEngine_MM();
4306   }
4307   else
4308   {
4309     DrawLevel(REDRAW_FIELD);
4310     DrawAllPlayers();
4311
4312     // after drawing the level, correct some elements
4313     if (game.timegate_time_left == 0)
4314       CloseAllOpenTimegates();
4315   }
4316
4317   // blit playfield from scroll buffer to normal back buffer for fading in
4318   BlitScreenToBitmap(backbuffer);
4319   // !!! FIX THIS (END) !!!
4320
4321   DrawMaskedBorder(fade_mask);
4322
4323   FadeIn(fade_mask);
4324
4325 #if 1
4326   // full screen redraw is required at this point in the following cases:
4327   // - special editor door undrawn when game was started from level editor
4328   // - drawing area (playfield) was changed and has to be removed completely
4329   redraw_mask = REDRAW_ALL;
4330   BackToFront();
4331 #endif
4332
4333   if (!game.restart_level)
4334   {
4335     // copy default game door content to main double buffer
4336
4337     // !!! CHECK AGAIN !!!
4338     SetPanelBackground();
4339     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4340     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4341   }
4342
4343   SetPanelBackground();
4344   SetDrawBackgroundMask(REDRAW_DOOR_1);
4345
4346   UpdateAndDisplayGameControlValues();
4347
4348   if (!game.restart_level)
4349   {
4350     UnmapGameButtons();
4351     UnmapTapeButtons();
4352
4353     FreeGameButtons();
4354     CreateGameButtons();
4355
4356     MapGameButtons();
4357     MapTapeButtons();
4358
4359     // copy actual game door content to door double buffer for OpenDoor()
4360     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4361
4362     OpenDoor(DOOR_OPEN_ALL);
4363
4364     KeyboardAutoRepeatOffUnlessAutoplay();
4365
4366 #if DEBUG_INIT_PLAYER
4367     DebugPrintPlayerStatus("Player status (final)");
4368 #endif
4369   }
4370
4371   UnmapAllGadgets();
4372
4373   MapGameButtons();
4374   MapTapeButtons();
4375
4376   if (!game.restart_level && !tape.playing)
4377   {
4378     LevelStats_incPlayed(level_nr);
4379
4380     SaveLevelSetup_SeriesInfo();
4381   }
4382
4383   game.restart_level = FALSE;
4384   game.restart_game_message = NULL;
4385   game.request_active = FALSE;
4386
4387   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4388     InitGameActions_MM();
4389
4390   SaveEngineSnapshotToListInitial();
4391
4392   if (!game.restart_level)
4393   {
4394     PlaySound(SND_GAME_STARTING);
4395
4396     if (setup.sound_music)
4397       PlayLevelMusic();
4398   }
4399 }
4400
4401 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4402                         int actual_player_x, int actual_player_y)
4403 {
4404   // this is used for non-R'n'D game engines to update certain engine values
4405
4406   // needed to determine if sounds are played within the visible screen area
4407   scroll_x = actual_scroll_x;
4408   scroll_y = actual_scroll_y;
4409
4410   // needed to get player position for "follow finger" playing input method
4411   local_player->jx = actual_player_x;
4412   local_player->jy = actual_player_y;
4413 }
4414
4415 void InitMovDir(int x, int y)
4416 {
4417   int i, element = Tile[x][y];
4418   static int xy[4][2] =
4419   {
4420     {  0, +1 },
4421     { +1,  0 },
4422     {  0, -1 },
4423     { -1,  0 }
4424   };
4425   static int direction[3][4] =
4426   {
4427     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4428     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4429     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4430   };
4431
4432   switch (element)
4433   {
4434     case EL_BUG_RIGHT:
4435     case EL_BUG_UP:
4436     case EL_BUG_LEFT:
4437     case EL_BUG_DOWN:
4438       Tile[x][y] = EL_BUG;
4439       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4440       break;
4441
4442     case EL_SPACESHIP_RIGHT:
4443     case EL_SPACESHIP_UP:
4444     case EL_SPACESHIP_LEFT:
4445     case EL_SPACESHIP_DOWN:
4446       Tile[x][y] = EL_SPACESHIP;
4447       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4448       break;
4449
4450     case EL_BD_BUTTERFLY_RIGHT:
4451     case EL_BD_BUTTERFLY_UP:
4452     case EL_BD_BUTTERFLY_LEFT:
4453     case EL_BD_BUTTERFLY_DOWN:
4454       Tile[x][y] = EL_BD_BUTTERFLY;
4455       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4456       break;
4457
4458     case EL_BD_FIREFLY_RIGHT:
4459     case EL_BD_FIREFLY_UP:
4460     case EL_BD_FIREFLY_LEFT:
4461     case EL_BD_FIREFLY_DOWN:
4462       Tile[x][y] = EL_BD_FIREFLY;
4463       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4464       break;
4465
4466     case EL_PACMAN_RIGHT:
4467     case EL_PACMAN_UP:
4468     case EL_PACMAN_LEFT:
4469     case EL_PACMAN_DOWN:
4470       Tile[x][y] = EL_PACMAN;
4471       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4472       break;
4473
4474     case EL_YAMYAM_LEFT:
4475     case EL_YAMYAM_RIGHT:
4476     case EL_YAMYAM_UP:
4477     case EL_YAMYAM_DOWN:
4478       Tile[x][y] = EL_YAMYAM;
4479       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4480       break;
4481
4482     case EL_SP_SNIKSNAK:
4483       MovDir[x][y] = MV_UP;
4484       break;
4485
4486     case EL_SP_ELECTRON:
4487       MovDir[x][y] = MV_LEFT;
4488       break;
4489
4490     case EL_MOLE_LEFT:
4491     case EL_MOLE_RIGHT:
4492     case EL_MOLE_UP:
4493     case EL_MOLE_DOWN:
4494       Tile[x][y] = EL_MOLE;
4495       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4496       break;
4497
4498     case EL_SPRING_LEFT:
4499     case EL_SPRING_RIGHT:
4500       Tile[x][y] = EL_SPRING;
4501       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4502       break;
4503
4504     default:
4505       if (IS_CUSTOM_ELEMENT(element))
4506       {
4507         struct ElementInfo *ei = &element_info[element];
4508         int move_direction_initial = ei->move_direction_initial;
4509         int move_pattern = ei->move_pattern;
4510
4511         if (move_direction_initial == MV_START_PREVIOUS)
4512         {
4513           if (MovDir[x][y] != MV_NONE)
4514             return;
4515
4516           move_direction_initial = MV_START_AUTOMATIC;
4517         }
4518
4519         if (move_direction_initial == MV_START_RANDOM)
4520           MovDir[x][y] = 1 << RND(4);
4521         else if (move_direction_initial & MV_ANY_DIRECTION)
4522           MovDir[x][y] = move_direction_initial;
4523         else if (move_pattern == MV_ALL_DIRECTIONS ||
4524                  move_pattern == MV_TURNING_LEFT ||
4525                  move_pattern == MV_TURNING_RIGHT ||
4526                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4527                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4528                  move_pattern == MV_TURNING_RANDOM)
4529           MovDir[x][y] = 1 << RND(4);
4530         else if (move_pattern == MV_HORIZONTAL)
4531           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4532         else if (move_pattern == MV_VERTICAL)
4533           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4534         else if (move_pattern & MV_ANY_DIRECTION)
4535           MovDir[x][y] = element_info[element].move_pattern;
4536         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4537                  move_pattern == MV_ALONG_RIGHT_SIDE)
4538         {
4539           // use random direction as default start direction
4540           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4541             MovDir[x][y] = 1 << RND(4);
4542
4543           for (i = 0; i < NUM_DIRECTIONS; i++)
4544           {
4545             int x1 = x + xy[i][0];
4546             int y1 = y + xy[i][1];
4547
4548             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4549             {
4550               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4551                 MovDir[x][y] = direction[0][i];
4552               else
4553                 MovDir[x][y] = direction[1][i];
4554
4555               break;
4556             }
4557           }
4558         }                
4559       }
4560       else
4561       {
4562         MovDir[x][y] = 1 << RND(4);
4563
4564         if (element != EL_BUG &&
4565             element != EL_SPACESHIP &&
4566             element != EL_BD_BUTTERFLY &&
4567             element != EL_BD_FIREFLY)
4568           break;
4569
4570         for (i = 0; i < NUM_DIRECTIONS; i++)
4571         {
4572           int x1 = x + xy[i][0];
4573           int y1 = y + xy[i][1];
4574
4575           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4576           {
4577             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4578             {
4579               MovDir[x][y] = direction[0][i];
4580               break;
4581             }
4582             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4583                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4584             {
4585               MovDir[x][y] = direction[1][i];
4586               break;
4587             }
4588           }
4589         }
4590       }
4591       break;
4592   }
4593
4594   GfxDir[x][y] = MovDir[x][y];
4595 }
4596
4597 void InitAmoebaNr(int x, int y)
4598 {
4599   int i;
4600   int group_nr = AmoebaNeighbourNr(x, y);
4601
4602   if (group_nr == 0)
4603   {
4604     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4605     {
4606       if (AmoebaCnt[i] == 0)
4607       {
4608         group_nr = i;
4609         break;
4610       }
4611     }
4612   }
4613
4614   AmoebaNr[x][y] = group_nr;
4615   AmoebaCnt[group_nr]++;
4616   AmoebaCnt2[group_nr]++;
4617 }
4618
4619 static void LevelSolved(void)
4620 {
4621   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4622       game.players_still_needed > 0)
4623     return;
4624
4625   game.LevelSolved = TRUE;
4626   game.GameOver = TRUE;
4627
4628   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4629                       game_em.lev->score :
4630                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4631                       game_mm.score :
4632                       game.score);
4633   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4634                        MM_HEALTH(game_mm.laser_overload_value) :
4635                        game.health);
4636
4637   game.LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed : TimeLeft);
4638   game.LevelSolved_CountingScore = game.score_final;
4639   game.LevelSolved_CountingHealth = game.health_final;
4640 }
4641
4642 void GameWon(void)
4643 {
4644   static int time_count_steps;
4645   static int time, time_final;
4646   static int score, score_final;
4647   static int health, health_final;
4648   static int game_over_delay_1 = 0;
4649   static int game_over_delay_2 = 0;
4650   static int game_over_delay_3 = 0;
4651   int game_over_delay_value_1 = 50;
4652   int game_over_delay_value_2 = 25;
4653   int game_over_delay_value_3 = 50;
4654
4655   if (!game.LevelSolved_GameWon)
4656   {
4657     int i;
4658
4659     // do not start end game actions before the player stops moving (to exit)
4660     if (local_player->active && local_player->MovPos)
4661       return;
4662
4663     game.LevelSolved_GameWon = TRUE;
4664     game.LevelSolved_SaveTape = tape.recording;
4665     game.LevelSolved_SaveScore = !tape.playing;
4666
4667     if (!tape.playing)
4668     {
4669       LevelStats_incSolved(level_nr);
4670
4671       SaveLevelSetup_SeriesInfo();
4672     }
4673
4674     if (tape.auto_play)         // tape might already be stopped here
4675       tape.auto_play_level_solved = TRUE;
4676
4677     TapeStop();
4678
4679     game_over_delay_1 = 0;
4680     game_over_delay_2 = 0;
4681     game_over_delay_3 = game_over_delay_value_3;
4682
4683     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4684     score = score_final = game.score_final;
4685     health = health_final = game.health_final;
4686
4687     if (level.score[SC_TIME_BONUS] > 0)
4688     {
4689       if (TimeLeft > 0)
4690       {
4691         time_final = 0;
4692         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4693       }
4694       else if (game.no_time_limit && TimePlayed < 999)
4695       {
4696         time_final = 999;
4697         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4698       }
4699
4700       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4701
4702       game_over_delay_1 = game_over_delay_value_1;
4703
4704       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4705       {
4706         health_final = 0;
4707         score_final += health * level.score[SC_TIME_BONUS];
4708
4709         game_over_delay_2 = game_over_delay_value_2;
4710       }
4711
4712       game.score_final = score_final;
4713       game.health_final = health_final;
4714     }
4715
4716     if (level_editor_test_game)
4717     {
4718       time = time_final;
4719       score = score_final;
4720
4721       game.LevelSolved_CountingTime = time;
4722       game.LevelSolved_CountingScore = score;
4723
4724       game_panel_controls[GAME_PANEL_TIME].value = time;
4725       game_panel_controls[GAME_PANEL_SCORE].value = score;
4726
4727       DisplayGameControlValues();
4728     }
4729
4730     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4731     {
4732       // check if last player has left the level
4733       if (game.exit_x >= 0 &&
4734           game.exit_y >= 0)
4735       {
4736         int x = game.exit_x;
4737         int y = game.exit_y;
4738         int element = Tile[x][y];
4739
4740         // close exit door after last player
4741         if ((game.all_players_gone &&
4742              (element == EL_EXIT_OPEN ||
4743               element == EL_SP_EXIT_OPEN ||
4744               element == EL_STEEL_EXIT_OPEN)) ||
4745             element == EL_EM_EXIT_OPEN ||
4746             element == EL_EM_STEEL_EXIT_OPEN)
4747         {
4748
4749           Tile[x][y] =
4750             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4751              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4752              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4753              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4754              EL_EM_STEEL_EXIT_CLOSING);
4755
4756           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4757         }
4758
4759         // player disappears
4760         DrawLevelField(x, y);
4761       }
4762
4763       for (i = 0; i < MAX_PLAYERS; i++)
4764       {
4765         struct PlayerInfo *player = &stored_player[i];
4766
4767         if (player->present)
4768         {
4769           RemovePlayer(player);
4770
4771           // player disappears
4772           DrawLevelField(player->jx, player->jy);
4773         }
4774       }
4775     }
4776
4777     PlaySound(SND_GAME_WINNING);
4778   }
4779
4780   if (game_over_delay_1 > 0)
4781   {
4782     game_over_delay_1--;
4783
4784     return;
4785   }
4786
4787   if (time != time_final)
4788   {
4789     int time_to_go = ABS(time_final - time);
4790     int time_count_dir = (time < time_final ? +1 : -1);
4791
4792     if (time_to_go < time_count_steps)
4793       time_count_steps = 1;
4794
4795     time  += time_count_steps * time_count_dir;
4796     score += time_count_steps * level.score[SC_TIME_BONUS];
4797
4798     game.LevelSolved_CountingTime = time;
4799     game.LevelSolved_CountingScore = score;
4800
4801     game_panel_controls[GAME_PANEL_TIME].value = time;
4802     game_panel_controls[GAME_PANEL_SCORE].value = score;
4803
4804     DisplayGameControlValues();
4805
4806     if (time == time_final)
4807       StopSound(SND_GAME_LEVELTIME_BONUS);
4808     else if (setup.sound_loops)
4809       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4810     else
4811       PlaySound(SND_GAME_LEVELTIME_BONUS);
4812
4813     return;
4814   }
4815
4816   if (game_over_delay_2 > 0)
4817   {
4818     game_over_delay_2--;
4819
4820     return;
4821   }
4822
4823   if (health != health_final)
4824   {
4825     int health_count_dir = (health < health_final ? +1 : -1);
4826
4827     health += health_count_dir;
4828     score  += level.score[SC_TIME_BONUS];
4829
4830     game.LevelSolved_CountingHealth = health;
4831     game.LevelSolved_CountingScore = score;
4832
4833     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4834     game_panel_controls[GAME_PANEL_SCORE].value = score;
4835
4836     DisplayGameControlValues();
4837
4838     if (health == health_final)
4839       StopSound(SND_GAME_LEVELTIME_BONUS);
4840     else if (setup.sound_loops)
4841       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4842     else
4843       PlaySound(SND_GAME_LEVELTIME_BONUS);
4844
4845     return;
4846   }
4847
4848   game.panel.active = FALSE;
4849
4850   if (game_over_delay_3 > 0)
4851   {
4852     game_over_delay_3--;
4853
4854     return;
4855   }
4856
4857   GameEnd();
4858 }
4859
4860 void GameEnd(void)
4861 {
4862   // used instead of "level_nr" (needed for network games)
4863   int last_level_nr = levelset.level_nr;
4864   int hi_pos;
4865
4866   game.LevelSolved_GameEnd = TRUE;
4867
4868   if (game.LevelSolved_SaveTape)
4869   {
4870     // make sure that request dialog to save tape does not open door again
4871     if (!global.use_envelope_request)
4872       CloseDoor(DOOR_CLOSE_1);
4873
4874     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4875   }
4876
4877   // if no tape is to be saved, close both doors simultaneously
4878   CloseDoor(DOOR_CLOSE_ALL);
4879
4880   if (level_editor_test_game)
4881   {
4882     SetGameStatus(GAME_MODE_MAIN);
4883
4884     DrawMainMenu();
4885
4886     return;
4887   }
4888
4889   if (!game.LevelSolved_SaveScore)
4890   {
4891     SetGameStatus(GAME_MODE_MAIN);
4892
4893     DrawMainMenu();
4894
4895     return;
4896   }
4897
4898   if (level_nr == leveldir_current->handicap_level)
4899   {
4900     leveldir_current->handicap_level++;
4901
4902     SaveLevelSetup_SeriesInfo();
4903   }
4904
4905   if (setup.increment_levels &&
4906       level_nr < leveldir_current->last_level &&
4907       !network_playing)
4908   {
4909     level_nr++;         // advance to next level
4910     TapeErase();        // start with empty tape
4911
4912     if (setup.auto_play_next_level)
4913     {
4914       LoadLevel(level_nr);
4915
4916       SaveLevelSetup_SeriesInfo();
4917     }
4918   }
4919
4920   hi_pos = NewHiScore(last_level_nr);
4921
4922   if (hi_pos >= 0 && !setup.skip_scores_after_game)
4923   {
4924     SetGameStatus(GAME_MODE_SCORES);
4925
4926     DrawHallOfFame(last_level_nr, hi_pos);
4927   }
4928   else if (setup.auto_play_next_level && setup.increment_levels &&
4929            last_level_nr < leveldir_current->last_level &&
4930            !network_playing)
4931   {
4932     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4933   }
4934   else
4935   {
4936     SetGameStatus(GAME_MODE_MAIN);
4937
4938     DrawMainMenu();
4939   }
4940 }
4941
4942 int NewHiScore(int level_nr)
4943 {
4944   int k, l;
4945   int position = -1;
4946   boolean one_score_entry_per_name = !program.many_scores_per_name;
4947
4948   LoadScore(level_nr);
4949
4950   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4951       game.score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4952     return -1;
4953
4954   for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4955   {
4956     if (game.score_final > highscore[k].Score)
4957     {
4958       // player has made it to the hall of fame
4959
4960       if (k < MAX_SCORE_ENTRIES - 1)
4961       {
4962         int m = MAX_SCORE_ENTRIES - 1;
4963
4964         if (one_score_entry_per_name)
4965         {
4966           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4967             if (strEqual(setup.player_name, highscore[l].Name))
4968               m = l;
4969
4970           if (m == k)   // player's new highscore overwrites his old one
4971             goto put_into_list;
4972         }
4973
4974         for (l = m; l > k; l--)
4975         {
4976           strcpy(highscore[l].Name, highscore[l - 1].Name);
4977           highscore[l].Score = highscore[l - 1].Score;
4978         }
4979       }
4980
4981       put_into_list:
4982
4983       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4984       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4985       highscore[k].Score = game.score_final;
4986       position = k;
4987
4988       break;
4989     }
4990     else if (one_score_entry_per_name &&
4991              !strncmp(setup.player_name, highscore[k].Name,
4992                       MAX_PLAYER_NAME_LEN))
4993       break;    // player already there with a higher score
4994   }
4995
4996   if (position >= 0) 
4997     SaveScore(level_nr);
4998
4999   return position;
5000 }
5001
5002 static int getElementMoveStepsizeExt(int x, int y, int direction)
5003 {
5004   int element = Tile[x][y];
5005   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5006   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5007   int horiz_move = (dx != 0);
5008   int sign = (horiz_move ? dx : dy);
5009   int step = sign * element_info[element].move_stepsize;
5010
5011   // special values for move stepsize for spring and things on conveyor belt
5012   if (horiz_move)
5013   {
5014     if (CAN_FALL(element) &&
5015         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5016       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5017     else if (element == EL_SPRING)
5018       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5019   }
5020
5021   return step;
5022 }
5023
5024 static int getElementMoveStepsize(int x, int y)
5025 {
5026   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5027 }
5028
5029 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5030 {
5031   if (player->GfxAction != action || player->GfxDir != dir)
5032   {
5033     player->GfxAction = action;
5034     player->GfxDir = dir;
5035     player->Frame = 0;
5036     player->StepFrame = 0;
5037   }
5038 }
5039
5040 static void ResetGfxFrame(int x, int y)
5041 {
5042   // profiling showed that "autotest" spends 10~20% of its time in this function
5043   if (DrawingDeactivatedField())
5044     return;
5045
5046   int element = Tile[x][y];
5047   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5048
5049   if (graphic_info[graphic].anim_global_sync)
5050     GfxFrame[x][y] = FrameCounter;
5051   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5052     GfxFrame[x][y] = CustomValue[x][y];
5053   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5054     GfxFrame[x][y] = element_info[element].collect_score;
5055   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5056     GfxFrame[x][y] = ChangeDelay[x][y];
5057 }
5058
5059 static void ResetGfxAnimation(int x, int y)
5060 {
5061   GfxAction[x][y] = ACTION_DEFAULT;
5062   GfxDir[x][y] = MovDir[x][y];
5063   GfxFrame[x][y] = 0;
5064
5065   ResetGfxFrame(x, y);
5066 }
5067
5068 static void ResetRandomAnimationValue(int x, int y)
5069 {
5070   GfxRandom[x][y] = INIT_GFX_RANDOM();
5071 }
5072
5073 static void InitMovingField(int x, int y, int direction)
5074 {
5075   int element = Tile[x][y];
5076   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5077   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5078   int newx = x + dx;
5079   int newy = y + dy;
5080   boolean is_moving_before, is_moving_after;
5081
5082   // check if element was/is moving or being moved before/after mode change
5083   is_moving_before = (WasJustMoving[x][y] != 0);
5084   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5085
5086   // reset animation only for moving elements which change direction of moving
5087   // or which just started or stopped moving
5088   // (else CEs with property "can move" / "not moving" are reset each frame)
5089   if (is_moving_before != is_moving_after ||
5090       direction != MovDir[x][y])
5091     ResetGfxAnimation(x, y);
5092
5093   MovDir[x][y] = direction;
5094   GfxDir[x][y] = direction;
5095
5096   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5097                      direction == MV_DOWN && CAN_FALL(element) ?
5098                      ACTION_FALLING : ACTION_MOVING);
5099
5100   // this is needed for CEs with property "can move" / "not moving"
5101
5102   if (is_moving_after)
5103   {
5104     if (Tile[newx][newy] == EL_EMPTY)
5105       Tile[newx][newy] = EL_BLOCKED;
5106
5107     MovDir[newx][newy] = MovDir[x][y];
5108
5109     CustomValue[newx][newy] = CustomValue[x][y];
5110
5111     GfxFrame[newx][newy] = GfxFrame[x][y];
5112     GfxRandom[newx][newy] = GfxRandom[x][y];
5113     GfxAction[newx][newy] = GfxAction[x][y];
5114     GfxDir[newx][newy] = GfxDir[x][y];
5115   }
5116 }
5117
5118 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5119 {
5120   int direction = MovDir[x][y];
5121   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5122   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5123
5124   *goes_to_x = newx;
5125   *goes_to_y = newy;
5126 }
5127
5128 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5129 {
5130   int oldx = x, oldy = y;
5131   int direction = MovDir[x][y];
5132
5133   if (direction == MV_LEFT)
5134     oldx++;
5135   else if (direction == MV_RIGHT)
5136     oldx--;
5137   else if (direction == MV_UP)
5138     oldy++;
5139   else if (direction == MV_DOWN)
5140     oldy--;
5141
5142   *comes_from_x = oldx;
5143   *comes_from_y = oldy;
5144 }
5145
5146 static int MovingOrBlocked2Element(int x, int y)
5147 {
5148   int element = Tile[x][y];
5149
5150   if (element == EL_BLOCKED)
5151   {
5152     int oldx, oldy;
5153
5154     Blocked2Moving(x, y, &oldx, &oldy);
5155     return Tile[oldx][oldy];
5156   }
5157   else
5158     return element;
5159 }
5160
5161 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5162 {
5163   // like MovingOrBlocked2Element(), but if element is moving
5164   // and (x,y) is the field the moving element is just leaving,
5165   // return EL_BLOCKED instead of the element value
5166   int element = Tile[x][y];
5167
5168   if (IS_MOVING(x, y))
5169   {
5170     if (element == EL_BLOCKED)
5171     {
5172       int oldx, oldy;
5173
5174       Blocked2Moving(x, y, &oldx, &oldy);
5175       return Tile[oldx][oldy];
5176     }
5177     else
5178       return EL_BLOCKED;
5179   }
5180   else
5181     return element;
5182 }
5183
5184 static void RemoveField(int x, int y)
5185 {
5186   Tile[x][y] = EL_EMPTY;
5187
5188   MovPos[x][y] = 0;
5189   MovDir[x][y] = 0;
5190   MovDelay[x][y] = 0;
5191
5192   CustomValue[x][y] = 0;
5193
5194   AmoebaNr[x][y] = 0;
5195   ChangeDelay[x][y] = 0;
5196   ChangePage[x][y] = -1;
5197   Pushed[x][y] = FALSE;
5198
5199   GfxElement[x][y] = EL_UNDEFINED;
5200   GfxAction[x][y] = ACTION_DEFAULT;
5201   GfxDir[x][y] = MV_NONE;
5202 }
5203
5204 static void RemoveMovingField(int x, int y)
5205 {
5206   int oldx = x, oldy = y, newx = x, newy = y;
5207   int element = Tile[x][y];
5208   int next_element = EL_UNDEFINED;
5209
5210   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5211     return;
5212
5213   if (IS_MOVING(x, y))
5214   {
5215     Moving2Blocked(x, y, &newx, &newy);
5216
5217     if (Tile[newx][newy] != EL_BLOCKED)
5218     {
5219       // element is moving, but target field is not free (blocked), but
5220       // already occupied by something different (example: acid pool);
5221       // in this case, only remove the moving field, but not the target
5222
5223       RemoveField(oldx, oldy);
5224
5225       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5226
5227       TEST_DrawLevelField(oldx, oldy);
5228
5229       return;
5230     }
5231   }
5232   else if (element == EL_BLOCKED)
5233   {
5234     Blocked2Moving(x, y, &oldx, &oldy);
5235     if (!IS_MOVING(oldx, oldy))
5236       return;
5237   }
5238
5239   if (element == EL_BLOCKED &&
5240       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5241        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5242        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5243        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5244        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5245        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5246     next_element = get_next_element(Tile[oldx][oldy]);
5247
5248   RemoveField(oldx, oldy);
5249   RemoveField(newx, newy);
5250
5251   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5252
5253   if (next_element != EL_UNDEFINED)
5254     Tile[oldx][oldy] = next_element;
5255
5256   TEST_DrawLevelField(oldx, oldy);
5257   TEST_DrawLevelField(newx, newy);
5258 }
5259
5260 void DrawDynamite(int x, int y)
5261 {
5262   int sx = SCREENX(x), sy = SCREENY(y);
5263   int graphic = el2img(Tile[x][y]);
5264   int frame;
5265
5266   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5267     return;
5268
5269   if (IS_WALKABLE_INSIDE(Back[x][y]))
5270     return;
5271
5272   if (Back[x][y])
5273     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5274   else if (Store[x][y])
5275     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5276
5277   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5278
5279   if (Back[x][y] || Store[x][y])
5280     DrawGraphicThruMask(sx, sy, graphic, frame);
5281   else
5282     DrawGraphic(sx, sy, graphic, frame);
5283 }
5284
5285 static void CheckDynamite(int x, int y)
5286 {
5287   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5288   {
5289     MovDelay[x][y]--;
5290
5291     if (MovDelay[x][y] != 0)
5292     {
5293       DrawDynamite(x, y);
5294       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5295
5296       return;
5297     }
5298   }
5299
5300   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5301
5302   Bang(x, y);
5303 }
5304
5305 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5306 {
5307   boolean num_checked_players = 0;
5308   int i;
5309
5310   for (i = 0; i < MAX_PLAYERS; i++)
5311   {
5312     if (stored_player[i].active)
5313     {
5314       int sx = stored_player[i].jx;
5315       int sy = stored_player[i].jy;
5316
5317       if (num_checked_players == 0)
5318       {
5319         *sx1 = *sx2 = sx;
5320         *sy1 = *sy2 = sy;
5321       }
5322       else
5323       {
5324         *sx1 = MIN(*sx1, sx);
5325         *sy1 = MIN(*sy1, sy);
5326         *sx2 = MAX(*sx2, sx);
5327         *sy2 = MAX(*sy2, sy);
5328       }
5329
5330       num_checked_players++;
5331     }
5332   }
5333 }
5334
5335 static boolean checkIfAllPlayersFitToScreen_RND(void)
5336 {
5337   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5338
5339   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5340
5341   return (sx2 - sx1 < SCR_FIELDX &&
5342           sy2 - sy1 < SCR_FIELDY);
5343 }
5344
5345 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5346 {
5347   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5348
5349   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5350
5351   *sx = (sx1 + sx2) / 2;
5352   *sy = (sy1 + sy2) / 2;
5353 }
5354
5355 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5356                                boolean center_screen, boolean quick_relocation)
5357 {
5358   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5359   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5360   boolean no_delay = (tape.warp_forward);
5361   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5362   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5363   int new_scroll_x, new_scroll_y;
5364
5365   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5366   {
5367     // case 1: quick relocation inside visible screen (without scrolling)
5368
5369     RedrawPlayfield();
5370
5371     return;
5372   }
5373
5374   if (!level.shifted_relocation || center_screen)
5375   {
5376     // relocation _with_ centering of screen
5377
5378     new_scroll_x = SCROLL_POSITION_X(x);
5379     new_scroll_y = SCROLL_POSITION_Y(y);
5380   }
5381   else
5382   {
5383     // relocation _without_ centering of screen
5384
5385     int center_scroll_x = SCROLL_POSITION_X(old_x);
5386     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5387     int offset_x = x + (scroll_x - center_scroll_x);
5388     int offset_y = y + (scroll_y - center_scroll_y);
5389
5390     // for new screen position, apply previous offset to center position
5391     new_scroll_x = SCROLL_POSITION_X(offset_x);
5392     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5393   }
5394
5395   if (quick_relocation)
5396   {
5397     // case 2: quick relocation (redraw without visible scrolling)
5398
5399     scroll_x = new_scroll_x;
5400     scroll_y = new_scroll_y;
5401
5402     RedrawPlayfield();
5403
5404     return;
5405   }
5406
5407   // case 3: visible relocation (with scrolling to new position)
5408
5409   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5410
5411   SetVideoFrameDelay(wait_delay_value);
5412
5413   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5414   {
5415     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5416     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5417
5418     if (dx == 0 && dy == 0)             // no scrolling needed at all
5419       break;
5420
5421     scroll_x -= dx;
5422     scroll_y -= dy;
5423
5424     // set values for horizontal/vertical screen scrolling (half tile size)
5425     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5426     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5427     int pos_x = dx * TILEX / 2;
5428     int pos_y = dy * TILEY / 2;
5429     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5430     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5431
5432     ScrollLevel(dx, dy);
5433     DrawAllPlayers();
5434
5435     // scroll in two steps of half tile size to make things smoother
5436     BlitScreenToBitmapExt_RND(window, fx, fy);
5437
5438     // scroll second step to align at full tile size
5439     BlitScreenToBitmap(window);
5440   }
5441
5442   DrawAllPlayers();
5443   BackToFront();
5444
5445   SetVideoFrameDelay(frame_delay_value_old);
5446 }
5447
5448 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5449 {
5450   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5451   int player_nr = GET_PLAYER_NR(el_player);
5452   struct PlayerInfo *player = &stored_player[player_nr];
5453   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5454   boolean no_delay = (tape.warp_forward);
5455   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5456   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5457   int old_jx = player->jx;
5458   int old_jy = player->jy;
5459   int old_element = Tile[old_jx][old_jy];
5460   int element = Tile[jx][jy];
5461   boolean player_relocated = (old_jx != jx || old_jy != jy);
5462
5463   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5464   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5465   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5466   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5467   int leave_side_horiz = move_dir_horiz;
5468   int leave_side_vert  = move_dir_vert;
5469   int enter_side = enter_side_horiz | enter_side_vert;
5470   int leave_side = leave_side_horiz | leave_side_vert;
5471
5472   if (player->buried)           // do not reanimate dead player
5473     return;
5474
5475   if (!player_relocated)        // no need to relocate the player
5476     return;
5477
5478   if (IS_PLAYER(jx, jy))        // player already placed at new position
5479   {
5480     RemoveField(jx, jy);        // temporarily remove newly placed player
5481     DrawLevelField(jx, jy);
5482   }
5483
5484   if (player->present)
5485   {
5486     while (player->MovPos)
5487     {
5488       ScrollPlayer(player, SCROLL_GO_ON);
5489       ScrollScreen(NULL, SCROLL_GO_ON);
5490
5491       AdvanceFrameAndPlayerCounters(player->index_nr);
5492
5493       DrawPlayer(player);
5494
5495       BackToFront_WithFrameDelay(wait_delay_value);
5496     }
5497
5498     DrawPlayer(player);         // needed here only to cleanup last field
5499     DrawLevelField(player->jx, player->jy);     // remove player graphic
5500
5501     player->is_moving = FALSE;
5502   }
5503
5504   if (IS_CUSTOM_ELEMENT(old_element))
5505     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5506                                CE_LEFT_BY_PLAYER,
5507                                player->index_bit, leave_side);
5508
5509   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5510                                       CE_PLAYER_LEAVES_X,
5511                                       player->index_bit, leave_side);
5512
5513   Tile[jx][jy] = el_player;
5514   InitPlayerField(jx, jy, el_player, TRUE);
5515
5516   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5517      possible that the relocation target field did not contain a player element,
5518      but a walkable element, to which the new player was relocated -- in this
5519      case, restore that (already initialized!) element on the player field */
5520   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5521   {
5522     Tile[jx][jy] = element;     // restore previously existing element
5523   }
5524
5525   // only visually relocate centered player
5526   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5527                      FALSE, level.instant_relocation);
5528
5529   TestIfPlayerTouchesBadThing(jx, jy);
5530   TestIfPlayerTouchesCustomElement(jx, jy);
5531
5532   if (IS_CUSTOM_ELEMENT(element))
5533     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5534                                player->index_bit, enter_side);
5535
5536   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5537                                       player->index_bit, enter_side);
5538
5539   if (player->is_switching)
5540   {
5541     /* ensure that relocation while still switching an element does not cause
5542        a new element to be treated as also switched directly after relocation
5543        (this is important for teleporter switches that teleport the player to
5544        a place where another teleporter switch is in the same direction, which
5545        would then incorrectly be treated as immediately switched before the
5546        direction key that caused the switch was released) */
5547
5548     player->switch_x += jx - old_jx;
5549     player->switch_y += jy - old_jy;
5550   }
5551 }
5552
5553 static void Explode(int ex, int ey, int phase, int mode)
5554 {
5555   int x, y;
5556   int last_phase;
5557   int border_element;
5558
5559   // !!! eliminate this variable !!!
5560   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5561
5562   if (game.explosions_delayed)
5563   {
5564     ExplodeField[ex][ey] = mode;
5565     return;
5566   }
5567
5568   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5569   {
5570     int center_element = Tile[ex][ey];
5571     int artwork_element, explosion_element;     // set these values later
5572
5573     // remove things displayed in background while burning dynamite
5574     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5575       Back[ex][ey] = 0;
5576
5577     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5578     {
5579       // put moving element to center field (and let it explode there)
5580       center_element = MovingOrBlocked2Element(ex, ey);
5581       RemoveMovingField(ex, ey);
5582       Tile[ex][ey] = center_element;
5583     }
5584
5585     // now "center_element" is finally determined -- set related values now
5586     artwork_element = center_element;           // for custom player artwork
5587     explosion_element = center_element;         // for custom player artwork
5588
5589     if (IS_PLAYER(ex, ey))
5590     {
5591       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5592
5593       artwork_element = stored_player[player_nr].artwork_element;
5594
5595       if (level.use_explosion_element[player_nr])
5596       {
5597         explosion_element = level.explosion_element[player_nr];
5598         artwork_element = explosion_element;
5599       }
5600     }
5601
5602     if (mode == EX_TYPE_NORMAL ||
5603         mode == EX_TYPE_CENTER ||
5604         mode == EX_TYPE_CROSS)
5605       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5606
5607     last_phase = element_info[explosion_element].explosion_delay + 1;
5608
5609     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5610     {
5611       int xx = x - ex + 1;
5612       int yy = y - ey + 1;
5613       int element;
5614
5615       if (!IN_LEV_FIELD(x, y) ||
5616           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5617           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5618         continue;
5619
5620       element = Tile[x][y];
5621
5622       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5623       {
5624         element = MovingOrBlocked2Element(x, y);
5625
5626         if (!IS_EXPLOSION_PROOF(element))
5627           RemoveMovingField(x, y);
5628       }
5629
5630       // indestructible elements can only explode in center (but not flames)
5631       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5632                                            mode == EX_TYPE_BORDER)) ||
5633           element == EL_FLAMES)
5634         continue;
5635
5636       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5637          behaviour, for example when touching a yamyam that explodes to rocks
5638          with active deadly shield, a rock is created under the player !!! */
5639       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5640 #if 0
5641       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5642           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5643            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5644 #else
5645       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5646 #endif
5647       {
5648         if (IS_ACTIVE_BOMB(element))
5649         {
5650           // re-activate things under the bomb like gate or penguin
5651           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5652           Back[x][y] = 0;
5653         }
5654
5655         continue;
5656       }
5657
5658       // save walkable background elements while explosion on same tile
5659       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5660           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5661         Back[x][y] = element;
5662
5663       // ignite explodable elements reached by other explosion
5664       if (element == EL_EXPLOSION)
5665         element = Store2[x][y];
5666
5667       if (AmoebaNr[x][y] &&
5668           (element == EL_AMOEBA_FULL ||
5669            element == EL_BD_AMOEBA ||
5670            element == EL_AMOEBA_GROWING))
5671       {
5672         AmoebaCnt[AmoebaNr[x][y]]--;
5673         AmoebaCnt2[AmoebaNr[x][y]]--;
5674       }
5675
5676       RemoveField(x, y);
5677
5678       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5679       {
5680         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5681
5682         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5683
5684         if (PLAYERINFO(ex, ey)->use_murphy)
5685           Store[x][y] = EL_EMPTY;
5686       }
5687
5688       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5689       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5690       else if (ELEM_IS_PLAYER(center_element))
5691         Store[x][y] = EL_EMPTY;
5692       else if (center_element == EL_YAMYAM)
5693         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5694       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5695         Store[x][y] = element_info[center_element].content.e[xx][yy];
5696 #if 1
5697       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5698       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5699       // otherwise) -- FIX THIS !!!
5700       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5701         Store[x][y] = element_info[element].content.e[1][1];
5702 #else
5703       else if (!CAN_EXPLODE(element))
5704         Store[x][y] = element_info[element].content.e[1][1];
5705 #endif
5706       else
5707         Store[x][y] = EL_EMPTY;
5708
5709       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5710           center_element == EL_AMOEBA_TO_DIAMOND)
5711         Store2[x][y] = element;
5712
5713       Tile[x][y] = EL_EXPLOSION;
5714       GfxElement[x][y] = artwork_element;
5715
5716       ExplodePhase[x][y] = 1;
5717       ExplodeDelay[x][y] = last_phase;
5718
5719       Stop[x][y] = TRUE;
5720     }
5721
5722     if (center_element == EL_YAMYAM)
5723       game.yamyam_content_nr =
5724         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5725
5726     return;
5727   }
5728
5729   if (Stop[ex][ey])
5730     return;
5731
5732   x = ex;
5733   y = ey;
5734
5735   if (phase == 1)
5736     GfxFrame[x][y] = 0;         // restart explosion animation
5737
5738   last_phase = ExplodeDelay[x][y];
5739
5740   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5741
5742   // this can happen if the player leaves an explosion just in time
5743   if (GfxElement[x][y] == EL_UNDEFINED)
5744     GfxElement[x][y] = EL_EMPTY;
5745
5746   border_element = Store2[x][y];
5747   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5748     border_element = StorePlayer[x][y];
5749
5750   if (phase == element_info[border_element].ignition_delay ||
5751       phase == last_phase)
5752   {
5753     boolean border_explosion = FALSE;
5754
5755     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5756         !PLAYER_EXPLOSION_PROTECTED(x, y))
5757     {
5758       KillPlayerUnlessExplosionProtected(x, y);
5759       border_explosion = TRUE;
5760     }
5761     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5762     {
5763       Tile[x][y] = Store2[x][y];
5764       Store2[x][y] = 0;
5765       Bang(x, y);
5766       border_explosion = TRUE;
5767     }
5768     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5769     {
5770       AmoebaToDiamond(x, y);
5771       Store2[x][y] = 0;
5772       border_explosion = TRUE;
5773     }
5774
5775     // if an element just explodes due to another explosion (chain-reaction),
5776     // do not immediately end the new explosion when it was the last frame of
5777     // the explosion (as it would be done in the following "if"-statement!)
5778     if (border_explosion && phase == last_phase)
5779       return;
5780   }
5781
5782   if (phase == last_phase)
5783   {
5784     int element;
5785
5786     element = Tile[x][y] = Store[x][y];
5787     Store[x][y] = Store2[x][y] = 0;
5788     GfxElement[x][y] = EL_UNDEFINED;
5789
5790     // player can escape from explosions and might therefore be still alive
5791     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5792         element <= EL_PLAYER_IS_EXPLODING_4)
5793     {
5794       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5795       int explosion_element = EL_PLAYER_1 + player_nr;
5796       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5797       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5798
5799       if (level.use_explosion_element[player_nr])
5800         explosion_element = level.explosion_element[player_nr];
5801
5802       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5803                     element_info[explosion_element].content.e[xx][yy]);
5804     }
5805
5806     // restore probably existing indestructible background element
5807     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5808       element = Tile[x][y] = Back[x][y];
5809     Back[x][y] = 0;
5810
5811     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5812     GfxDir[x][y] = MV_NONE;
5813     ChangeDelay[x][y] = 0;
5814     ChangePage[x][y] = -1;
5815
5816     CustomValue[x][y] = 0;
5817
5818     InitField_WithBug2(x, y, FALSE);
5819
5820     TEST_DrawLevelField(x, y);
5821
5822     TestIfElementTouchesCustomElement(x, y);
5823
5824     if (GFX_CRUMBLED(element))
5825       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5826
5827     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5828       StorePlayer[x][y] = 0;
5829
5830     if (ELEM_IS_PLAYER(element))
5831       RelocatePlayer(x, y, element);
5832   }
5833   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5834   {
5835     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5836     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5837
5838     if (phase == delay)
5839       TEST_DrawLevelFieldCrumbled(x, y);
5840
5841     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5842     {
5843       DrawLevelElement(x, y, Back[x][y]);
5844       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5845     }
5846     else if (IS_WALKABLE_UNDER(Back[x][y]))
5847     {
5848       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5849       DrawLevelElementThruMask(x, y, Back[x][y]);
5850     }
5851     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5852       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5853   }
5854 }
5855
5856 static void DynaExplode(int ex, int ey)
5857 {
5858   int i, j;
5859   int dynabomb_element = Tile[ex][ey];
5860   int dynabomb_size = 1;
5861   boolean dynabomb_xl = FALSE;
5862   struct PlayerInfo *player;
5863   static int xy[4][2] =
5864   {
5865     { 0, -1 },
5866     { -1, 0 },
5867     { +1, 0 },
5868     { 0, +1 }
5869   };
5870
5871   if (IS_ACTIVE_BOMB(dynabomb_element))
5872   {
5873     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5874     dynabomb_size = player->dynabomb_size;
5875     dynabomb_xl = player->dynabomb_xl;
5876     player->dynabombs_left++;
5877   }
5878
5879   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5880
5881   for (i = 0; i < NUM_DIRECTIONS; i++)
5882   {
5883     for (j = 1; j <= dynabomb_size; j++)
5884     {
5885       int x = ex + j * xy[i][0];
5886       int y = ey + j * xy[i][1];
5887       int element;
5888
5889       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
5890         break;
5891
5892       element = Tile[x][y];
5893
5894       // do not restart explosions of fields with active bombs
5895       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5896         continue;
5897
5898       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5899
5900       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5901           !IS_DIGGABLE(element) && !dynabomb_xl)
5902         break;
5903     }
5904   }
5905 }
5906
5907 void Bang(int x, int y)
5908 {
5909   int element = MovingOrBlocked2Element(x, y);
5910   int explosion_type = EX_TYPE_NORMAL;
5911
5912   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5913   {
5914     struct PlayerInfo *player = PLAYERINFO(x, y);
5915
5916     element = Tile[x][y] = player->initial_element;
5917
5918     if (level.use_explosion_element[player->index_nr])
5919     {
5920       int explosion_element = level.explosion_element[player->index_nr];
5921
5922       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5923         explosion_type = EX_TYPE_CROSS;
5924       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5925         explosion_type = EX_TYPE_CENTER;
5926     }
5927   }
5928
5929   switch (element)
5930   {
5931     case EL_BUG:
5932     case EL_SPACESHIP:
5933     case EL_BD_BUTTERFLY:
5934     case EL_BD_FIREFLY:
5935     case EL_YAMYAM:
5936     case EL_DARK_YAMYAM:
5937     case EL_ROBOT:
5938     case EL_PACMAN:
5939     case EL_MOLE:
5940       RaiseScoreElement(element);
5941       break;
5942
5943     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5944     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5945     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5946     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5947     case EL_DYNABOMB_INCREASE_NUMBER:
5948     case EL_DYNABOMB_INCREASE_SIZE:
5949     case EL_DYNABOMB_INCREASE_POWER:
5950       explosion_type = EX_TYPE_DYNA;
5951       break;
5952
5953     case EL_DC_LANDMINE:
5954       explosion_type = EX_TYPE_CENTER;
5955       break;
5956
5957     case EL_PENGUIN:
5958     case EL_LAMP:
5959     case EL_LAMP_ACTIVE:
5960     case EL_AMOEBA_TO_DIAMOND:
5961       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
5962         explosion_type = EX_TYPE_CENTER;
5963       break;
5964
5965     default:
5966       if (element_info[element].explosion_type == EXPLODES_CROSS)
5967         explosion_type = EX_TYPE_CROSS;
5968       else if (element_info[element].explosion_type == EXPLODES_1X1)
5969         explosion_type = EX_TYPE_CENTER;
5970       break;
5971   }
5972
5973   if (explosion_type == EX_TYPE_DYNA)
5974     DynaExplode(x, y);
5975   else
5976     Explode(x, y, EX_PHASE_START, explosion_type);
5977
5978   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5979 }
5980
5981 static void SplashAcid(int x, int y)
5982 {
5983   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5984       (!IN_LEV_FIELD(x - 1, y - 2) ||
5985        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5986     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5987
5988   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5989       (!IN_LEV_FIELD(x + 1, y - 2) ||
5990        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5991     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5992
5993   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5994 }
5995
5996 static void InitBeltMovement(void)
5997 {
5998   static int belt_base_element[4] =
5999   {
6000     EL_CONVEYOR_BELT_1_LEFT,
6001     EL_CONVEYOR_BELT_2_LEFT,
6002     EL_CONVEYOR_BELT_3_LEFT,
6003     EL_CONVEYOR_BELT_4_LEFT
6004   };
6005   static int belt_base_active_element[4] =
6006   {
6007     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6008     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6009     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6010     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6011   };
6012
6013   int x, y, i, j;
6014
6015   // set frame order for belt animation graphic according to belt direction
6016   for (i = 0; i < NUM_BELTS; i++)
6017   {
6018     int belt_nr = i;
6019
6020     for (j = 0; j < NUM_BELT_PARTS; j++)
6021     {
6022       int element = belt_base_active_element[belt_nr] + j;
6023       int graphic_1 = el2img(element);
6024       int graphic_2 = el2panelimg(element);
6025
6026       if (game.belt_dir[i] == MV_LEFT)
6027       {
6028         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6029         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6030       }
6031       else
6032       {
6033         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6034         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6035       }
6036     }
6037   }
6038
6039   SCAN_PLAYFIELD(x, y)
6040   {
6041     int element = Tile[x][y];
6042
6043     for (i = 0; i < NUM_BELTS; i++)
6044     {
6045       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6046       {
6047         int e_belt_nr = getBeltNrFromBeltElement(element);
6048         int belt_nr = i;
6049
6050         if (e_belt_nr == belt_nr)
6051         {
6052           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6053
6054           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6055         }
6056       }
6057     }
6058   }
6059 }
6060
6061 static void ToggleBeltSwitch(int x, int y)
6062 {
6063   static int belt_base_element[4] =
6064   {
6065     EL_CONVEYOR_BELT_1_LEFT,
6066     EL_CONVEYOR_BELT_2_LEFT,
6067     EL_CONVEYOR_BELT_3_LEFT,
6068     EL_CONVEYOR_BELT_4_LEFT
6069   };
6070   static int belt_base_active_element[4] =
6071   {
6072     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6073     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6074     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6075     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6076   };
6077   static int belt_base_switch_element[4] =
6078   {
6079     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6080     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6081     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6082     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6083   };
6084   static int belt_move_dir[4] =
6085   {
6086     MV_LEFT,
6087     MV_NONE,
6088     MV_RIGHT,
6089     MV_NONE,
6090   };
6091
6092   int element = Tile[x][y];
6093   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6094   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6095   int belt_dir = belt_move_dir[belt_dir_nr];
6096   int xx, yy, i;
6097
6098   if (!IS_BELT_SWITCH(element))
6099     return;
6100
6101   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6102   game.belt_dir[belt_nr] = belt_dir;
6103
6104   if (belt_dir_nr == 3)
6105     belt_dir_nr = 1;
6106
6107   // set frame order for belt animation graphic according to belt direction
6108   for (i = 0; i < NUM_BELT_PARTS; i++)
6109   {
6110     int element = belt_base_active_element[belt_nr] + i;
6111     int graphic_1 = el2img(element);
6112     int graphic_2 = el2panelimg(element);
6113
6114     if (belt_dir == MV_LEFT)
6115     {
6116       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6117       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6118     }
6119     else
6120     {
6121       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6122       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6123     }
6124   }
6125
6126   SCAN_PLAYFIELD(xx, yy)
6127   {
6128     int element = Tile[xx][yy];
6129
6130     if (IS_BELT_SWITCH(element))
6131     {
6132       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6133
6134       if (e_belt_nr == belt_nr)
6135       {
6136         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6137         TEST_DrawLevelField(xx, yy);
6138       }
6139     }
6140     else if (IS_BELT(element) && belt_dir != MV_NONE)
6141     {
6142       int e_belt_nr = getBeltNrFromBeltElement(element);
6143
6144       if (e_belt_nr == belt_nr)
6145       {
6146         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6147
6148         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6149         TEST_DrawLevelField(xx, yy);
6150       }
6151     }
6152     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6153     {
6154       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6155
6156       if (e_belt_nr == belt_nr)
6157       {
6158         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6159
6160         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6161         TEST_DrawLevelField(xx, yy);
6162       }
6163     }
6164   }
6165 }
6166
6167 static void ToggleSwitchgateSwitch(int x, int y)
6168 {
6169   int xx, yy;
6170
6171   game.switchgate_pos = !game.switchgate_pos;
6172
6173   SCAN_PLAYFIELD(xx, yy)
6174   {
6175     int element = Tile[xx][yy];
6176
6177     if (element == EL_SWITCHGATE_SWITCH_UP)
6178     {
6179       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6180       TEST_DrawLevelField(xx, yy);
6181     }
6182     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6183     {
6184       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6185       TEST_DrawLevelField(xx, yy);
6186     }
6187     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6188     {
6189       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6190       TEST_DrawLevelField(xx, yy);
6191     }
6192     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6193     {
6194       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6195       TEST_DrawLevelField(xx, yy);
6196     }
6197     else if (element == EL_SWITCHGATE_OPEN ||
6198              element == EL_SWITCHGATE_OPENING)
6199     {
6200       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6201
6202       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6203     }
6204     else if (element == EL_SWITCHGATE_CLOSED ||
6205              element == EL_SWITCHGATE_CLOSING)
6206     {
6207       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6208
6209       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6210     }
6211   }
6212 }
6213
6214 static int getInvisibleActiveFromInvisibleElement(int element)
6215 {
6216   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6217           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6218           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6219           element);
6220 }
6221
6222 static int getInvisibleFromInvisibleActiveElement(int element)
6223 {
6224   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6225           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6226           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6227           element);
6228 }
6229
6230 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6231 {
6232   int x, y;
6233
6234   SCAN_PLAYFIELD(x, y)
6235   {
6236     int element = Tile[x][y];
6237
6238     if (element == EL_LIGHT_SWITCH &&
6239         game.light_time_left > 0)
6240     {
6241       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6242       TEST_DrawLevelField(x, y);
6243     }
6244     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6245              game.light_time_left == 0)
6246     {
6247       Tile[x][y] = EL_LIGHT_SWITCH;
6248       TEST_DrawLevelField(x, y);
6249     }
6250     else if (element == EL_EMC_DRIPPER &&
6251              game.light_time_left > 0)
6252     {
6253       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6254       TEST_DrawLevelField(x, y);
6255     }
6256     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6257              game.light_time_left == 0)
6258     {
6259       Tile[x][y] = EL_EMC_DRIPPER;
6260       TEST_DrawLevelField(x, y);
6261     }
6262     else if (element == EL_INVISIBLE_STEELWALL ||
6263              element == EL_INVISIBLE_WALL ||
6264              element == EL_INVISIBLE_SAND)
6265     {
6266       if (game.light_time_left > 0)
6267         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6268
6269       TEST_DrawLevelField(x, y);
6270
6271       // uncrumble neighbour fields, if needed
6272       if (element == EL_INVISIBLE_SAND)
6273         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6274     }
6275     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6276              element == EL_INVISIBLE_WALL_ACTIVE ||
6277              element == EL_INVISIBLE_SAND_ACTIVE)
6278     {
6279       if (game.light_time_left == 0)
6280         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6281
6282       TEST_DrawLevelField(x, y);
6283
6284       // re-crumble neighbour fields, if needed
6285       if (element == EL_INVISIBLE_SAND)
6286         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6287     }
6288   }
6289 }
6290
6291 static void RedrawAllInvisibleElementsForLenses(void)
6292 {
6293   int x, y;
6294
6295   SCAN_PLAYFIELD(x, y)
6296   {
6297     int element = Tile[x][y];
6298
6299     if (element == EL_EMC_DRIPPER &&
6300         game.lenses_time_left > 0)
6301     {
6302       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6303       TEST_DrawLevelField(x, y);
6304     }
6305     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6306              game.lenses_time_left == 0)
6307     {
6308       Tile[x][y] = EL_EMC_DRIPPER;
6309       TEST_DrawLevelField(x, y);
6310     }
6311     else if (element == EL_INVISIBLE_STEELWALL ||
6312              element == EL_INVISIBLE_WALL ||
6313              element == EL_INVISIBLE_SAND)
6314     {
6315       if (game.lenses_time_left > 0)
6316         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6317
6318       TEST_DrawLevelField(x, y);
6319
6320       // uncrumble neighbour fields, if needed
6321       if (element == EL_INVISIBLE_SAND)
6322         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6323     }
6324     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6325              element == EL_INVISIBLE_WALL_ACTIVE ||
6326              element == EL_INVISIBLE_SAND_ACTIVE)
6327     {
6328       if (game.lenses_time_left == 0)
6329         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6330
6331       TEST_DrawLevelField(x, y);
6332
6333       // re-crumble neighbour fields, if needed
6334       if (element == EL_INVISIBLE_SAND)
6335         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6336     }
6337   }
6338 }
6339
6340 static void RedrawAllInvisibleElementsForMagnifier(void)
6341 {
6342   int x, y;
6343
6344   SCAN_PLAYFIELD(x, y)
6345   {
6346     int element = Tile[x][y];
6347
6348     if (element == EL_EMC_FAKE_GRASS &&
6349         game.magnify_time_left > 0)
6350     {
6351       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6352       TEST_DrawLevelField(x, y);
6353     }
6354     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6355              game.magnify_time_left == 0)
6356     {
6357       Tile[x][y] = EL_EMC_FAKE_GRASS;
6358       TEST_DrawLevelField(x, y);
6359     }
6360     else if (IS_GATE_GRAY(element) &&
6361              game.magnify_time_left > 0)
6362     {
6363       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6364                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6365                     IS_EM_GATE_GRAY(element) ?
6366                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6367                     IS_EMC_GATE_GRAY(element) ?
6368                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6369                     IS_DC_GATE_GRAY(element) ?
6370                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6371                     element);
6372       TEST_DrawLevelField(x, y);
6373     }
6374     else if (IS_GATE_GRAY_ACTIVE(element) &&
6375              game.magnify_time_left == 0)
6376     {
6377       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6378                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6379                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6380                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6381                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6382                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6383                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6384                     EL_DC_GATE_WHITE_GRAY :
6385                     element);
6386       TEST_DrawLevelField(x, y);
6387     }
6388   }
6389 }
6390
6391 static void ToggleLightSwitch(int x, int y)
6392 {
6393   int element = Tile[x][y];
6394
6395   game.light_time_left =
6396     (element == EL_LIGHT_SWITCH ?
6397      level.time_light * FRAMES_PER_SECOND : 0);
6398
6399   RedrawAllLightSwitchesAndInvisibleElements();
6400 }
6401
6402 static void ActivateTimegateSwitch(int x, int y)
6403 {
6404   int xx, yy;
6405
6406   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6407
6408   SCAN_PLAYFIELD(xx, yy)
6409   {
6410     int element = Tile[xx][yy];
6411
6412     if (element == EL_TIMEGATE_CLOSED ||
6413         element == EL_TIMEGATE_CLOSING)
6414     {
6415       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6416       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6417     }
6418
6419     /*
6420     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6421     {
6422       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6423       TEST_DrawLevelField(xx, yy);
6424     }
6425     */
6426
6427   }
6428
6429   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6430                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6431 }
6432
6433 static void Impact(int x, int y)
6434 {
6435   boolean last_line = (y == lev_fieldy - 1);
6436   boolean object_hit = FALSE;
6437   boolean impact = (last_line || object_hit);
6438   int element = Tile[x][y];
6439   int smashed = EL_STEELWALL;
6440
6441   if (!last_line)       // check if element below was hit
6442   {
6443     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6444       return;
6445
6446     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6447                                          MovDir[x][y + 1] != MV_DOWN ||
6448                                          MovPos[x][y + 1] <= TILEY / 2));
6449
6450     // do not smash moving elements that left the smashed field in time
6451     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6452         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6453       object_hit = FALSE;
6454
6455 #if USE_QUICKSAND_IMPACT_BUGFIX
6456     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6457     {
6458       RemoveMovingField(x, y + 1);
6459       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6460       Tile[x][y + 2] = EL_ROCK;
6461       TEST_DrawLevelField(x, y + 2);
6462
6463       object_hit = TRUE;
6464     }
6465
6466     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6467     {
6468       RemoveMovingField(x, y + 1);
6469       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6470       Tile[x][y + 2] = EL_ROCK;
6471       TEST_DrawLevelField(x, y + 2);
6472
6473       object_hit = TRUE;
6474     }
6475 #endif
6476
6477     if (object_hit)
6478       smashed = MovingOrBlocked2Element(x, y + 1);
6479
6480     impact = (last_line || object_hit);
6481   }
6482
6483   if (!last_line && smashed == EL_ACID) // element falls into acid
6484   {
6485     SplashAcid(x, y + 1);
6486     return;
6487   }
6488
6489   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6490   // only reset graphic animation if graphic really changes after impact
6491   if (impact &&
6492       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6493   {
6494     ResetGfxAnimation(x, y);
6495     TEST_DrawLevelField(x, y);
6496   }
6497
6498   if (impact && CAN_EXPLODE_IMPACT(element))
6499   {
6500     Bang(x, y);
6501     return;
6502   }
6503   else if (impact && element == EL_PEARL &&
6504            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6505   {
6506     ResetGfxAnimation(x, y);
6507
6508     Tile[x][y] = EL_PEARL_BREAKING;
6509     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6510     return;
6511   }
6512   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6513   {
6514     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6515
6516     return;
6517   }
6518
6519   if (impact && element == EL_AMOEBA_DROP)
6520   {
6521     if (object_hit && IS_PLAYER(x, y + 1))
6522       KillPlayerUnlessEnemyProtected(x, y + 1);
6523     else if (object_hit && smashed == EL_PENGUIN)
6524       Bang(x, y + 1);
6525     else
6526     {
6527       Tile[x][y] = EL_AMOEBA_GROWING;
6528       Store[x][y] = EL_AMOEBA_WET;
6529
6530       ResetRandomAnimationValue(x, y);
6531     }
6532     return;
6533   }
6534
6535   if (object_hit)               // check which object was hit
6536   {
6537     if ((CAN_PASS_MAGIC_WALL(element) && 
6538          (smashed == EL_MAGIC_WALL ||
6539           smashed == EL_BD_MAGIC_WALL)) ||
6540         (CAN_PASS_DC_MAGIC_WALL(element) &&
6541          smashed == EL_DC_MAGIC_WALL))
6542     {
6543       int xx, yy;
6544       int activated_magic_wall =
6545         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6546          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6547          EL_DC_MAGIC_WALL_ACTIVE);
6548
6549       // activate magic wall / mill
6550       SCAN_PLAYFIELD(xx, yy)
6551       {
6552         if (Tile[xx][yy] == smashed)
6553           Tile[xx][yy] = activated_magic_wall;
6554       }
6555
6556       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6557       game.magic_wall_active = TRUE;
6558
6559       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6560                             SND_MAGIC_WALL_ACTIVATING :
6561                             smashed == EL_BD_MAGIC_WALL ?
6562                             SND_BD_MAGIC_WALL_ACTIVATING :
6563                             SND_DC_MAGIC_WALL_ACTIVATING));
6564     }
6565
6566     if (IS_PLAYER(x, y + 1))
6567     {
6568       if (CAN_SMASH_PLAYER(element))
6569       {
6570         KillPlayerUnlessEnemyProtected(x, y + 1);
6571         return;
6572       }
6573     }
6574     else if (smashed == EL_PENGUIN)
6575     {
6576       if (CAN_SMASH_PLAYER(element))
6577       {
6578         Bang(x, y + 1);
6579         return;
6580       }
6581     }
6582     else if (element == EL_BD_DIAMOND)
6583     {
6584       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6585       {
6586         Bang(x, y + 1);
6587         return;
6588       }
6589     }
6590     else if (((element == EL_SP_INFOTRON ||
6591                element == EL_SP_ZONK) &&
6592               (smashed == EL_SP_SNIKSNAK ||
6593                smashed == EL_SP_ELECTRON ||
6594                smashed == EL_SP_DISK_ORANGE)) ||
6595              (element == EL_SP_INFOTRON &&
6596               smashed == EL_SP_DISK_YELLOW))
6597     {
6598       Bang(x, y + 1);
6599       return;
6600     }
6601     else if (CAN_SMASH_EVERYTHING(element))
6602     {
6603       if (IS_CLASSIC_ENEMY(smashed) ||
6604           CAN_EXPLODE_SMASHED(smashed))
6605       {
6606         Bang(x, y + 1);
6607         return;
6608       }
6609       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6610       {
6611         if (smashed == EL_LAMP ||
6612             smashed == EL_LAMP_ACTIVE)
6613         {
6614           Bang(x, y + 1);
6615           return;
6616         }
6617         else if (smashed == EL_NUT)
6618         {
6619           Tile[x][y + 1] = EL_NUT_BREAKING;
6620           PlayLevelSound(x, y, SND_NUT_BREAKING);
6621           RaiseScoreElement(EL_NUT);
6622           return;
6623         }
6624         else if (smashed == EL_PEARL)
6625         {
6626           ResetGfxAnimation(x, y);
6627
6628           Tile[x][y + 1] = EL_PEARL_BREAKING;
6629           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6630           return;
6631         }
6632         else if (smashed == EL_DIAMOND)
6633         {
6634           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6635           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6636           return;
6637         }
6638         else if (IS_BELT_SWITCH(smashed))
6639         {
6640           ToggleBeltSwitch(x, y + 1);
6641         }
6642         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6643                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6644                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6645                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6646         {
6647           ToggleSwitchgateSwitch(x, y + 1);
6648         }
6649         else if (smashed == EL_LIGHT_SWITCH ||
6650                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6651         {
6652           ToggleLightSwitch(x, y + 1);
6653         }
6654         else
6655         {
6656           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6657
6658           CheckElementChangeBySide(x, y + 1, smashed, element,
6659                                    CE_SWITCHED, CH_SIDE_TOP);
6660           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6661                                             CH_SIDE_TOP);
6662         }
6663       }
6664       else
6665       {
6666         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6667       }
6668     }
6669   }
6670
6671   // play sound of magic wall / mill
6672   if (!last_line &&
6673       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6674        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6675        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6676   {
6677     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6678       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6679     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6680       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6681     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6682       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6683
6684     return;
6685   }
6686
6687   // play sound of object that hits the ground
6688   if (last_line || object_hit)
6689     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6690 }
6691
6692 static void TurnRoundExt(int x, int y)
6693 {
6694   static struct
6695   {
6696     int dx, dy;
6697   } move_xy[] =
6698   {
6699     {  0,  0 },
6700     { -1,  0 },
6701     { +1,  0 },
6702     {  0,  0 },
6703     {  0, -1 },
6704     {  0,  0 }, { 0, 0 }, { 0, 0 },
6705     {  0, +1 }
6706   };
6707   static struct
6708   {
6709     int left, right, back;
6710   } turn[] =
6711   {
6712     { 0,        0,              0        },
6713     { MV_DOWN,  MV_UP,          MV_RIGHT },
6714     { MV_UP,    MV_DOWN,        MV_LEFT  },
6715     { 0,        0,              0        },
6716     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6717     { 0,        0,              0        },
6718     { 0,        0,              0        },
6719     { 0,        0,              0        },
6720     { MV_RIGHT, MV_LEFT,        MV_UP    }
6721   };
6722
6723   int element = Tile[x][y];
6724   int move_pattern = element_info[element].move_pattern;
6725
6726   int old_move_dir = MovDir[x][y];
6727   int left_dir  = turn[old_move_dir].left;
6728   int right_dir = turn[old_move_dir].right;
6729   int back_dir  = turn[old_move_dir].back;
6730
6731   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6732   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6733   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6734   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6735
6736   int left_x  = x + left_dx,  left_y  = y + left_dy;
6737   int right_x = x + right_dx, right_y = y + right_dy;
6738   int move_x  = x + move_dx,  move_y  = y + move_dy;
6739
6740   int xx, yy;
6741
6742   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6743   {
6744     TestIfBadThingTouchesOtherBadThing(x, y);
6745
6746     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6747       MovDir[x][y] = right_dir;
6748     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6749       MovDir[x][y] = left_dir;
6750
6751     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6752       MovDelay[x][y] = 9;
6753     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6754       MovDelay[x][y] = 1;
6755   }
6756   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6757   {
6758     TestIfBadThingTouchesOtherBadThing(x, y);
6759
6760     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6761       MovDir[x][y] = left_dir;
6762     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6763       MovDir[x][y] = right_dir;
6764
6765     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6766       MovDelay[x][y] = 9;
6767     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6768       MovDelay[x][y] = 1;
6769   }
6770   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6771   {
6772     TestIfBadThingTouchesOtherBadThing(x, y);
6773
6774     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6775       MovDir[x][y] = left_dir;
6776     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6777       MovDir[x][y] = right_dir;
6778
6779     if (MovDir[x][y] != old_move_dir)
6780       MovDelay[x][y] = 9;
6781   }
6782   else if (element == EL_YAMYAM)
6783   {
6784     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6785     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6786
6787     if (can_turn_left && can_turn_right)
6788       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6789     else if (can_turn_left)
6790       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6791     else if (can_turn_right)
6792       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6793     else
6794       MovDir[x][y] = back_dir;
6795
6796     MovDelay[x][y] = 16 + 16 * RND(3);
6797   }
6798   else if (element == EL_DARK_YAMYAM)
6799   {
6800     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6801                                                          left_x, left_y);
6802     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6803                                                          right_x, right_y);
6804
6805     if (can_turn_left && can_turn_right)
6806       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6807     else if (can_turn_left)
6808       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6809     else if (can_turn_right)
6810       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6811     else
6812       MovDir[x][y] = back_dir;
6813
6814     MovDelay[x][y] = 16 + 16 * RND(3);
6815   }
6816   else if (element == EL_PACMAN)
6817   {
6818     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6819     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6820
6821     if (can_turn_left && can_turn_right)
6822       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6823     else if (can_turn_left)
6824       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6825     else if (can_turn_right)
6826       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6827     else
6828       MovDir[x][y] = back_dir;
6829
6830     MovDelay[x][y] = 6 + RND(40);
6831   }
6832   else if (element == EL_PIG)
6833   {
6834     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6835     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6836     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6837     boolean should_turn_left, should_turn_right, should_move_on;
6838     int rnd_value = 24;
6839     int rnd = RND(rnd_value);
6840
6841     should_turn_left = (can_turn_left &&
6842                         (!can_move_on ||
6843                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6844                                                    y + back_dy + left_dy)));
6845     should_turn_right = (can_turn_right &&
6846                          (!can_move_on ||
6847                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6848                                                     y + back_dy + right_dy)));
6849     should_move_on = (can_move_on &&
6850                       (!can_turn_left ||
6851                        !can_turn_right ||
6852                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6853                                                  y + move_dy + left_dy) ||
6854                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6855                                                  y + move_dy + right_dy)));
6856
6857     if (should_turn_left || should_turn_right || should_move_on)
6858     {
6859       if (should_turn_left && should_turn_right && should_move_on)
6860         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6861                         rnd < 2 * rnd_value / 3 ? right_dir :
6862                         old_move_dir);
6863       else if (should_turn_left && should_turn_right)
6864         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6865       else if (should_turn_left && should_move_on)
6866         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6867       else if (should_turn_right && should_move_on)
6868         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6869       else if (should_turn_left)
6870         MovDir[x][y] = left_dir;
6871       else if (should_turn_right)
6872         MovDir[x][y] = right_dir;
6873       else if (should_move_on)
6874         MovDir[x][y] = old_move_dir;
6875     }
6876     else if (can_move_on && rnd > rnd_value / 8)
6877       MovDir[x][y] = old_move_dir;
6878     else if (can_turn_left && can_turn_right)
6879       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6880     else if (can_turn_left && rnd > rnd_value / 8)
6881       MovDir[x][y] = left_dir;
6882     else if (can_turn_right && rnd > rnd_value/8)
6883       MovDir[x][y] = right_dir;
6884     else
6885       MovDir[x][y] = back_dir;
6886
6887     xx = x + move_xy[MovDir[x][y]].dx;
6888     yy = y + move_xy[MovDir[x][y]].dy;
6889
6890     if (!IN_LEV_FIELD(xx, yy) ||
6891         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
6892       MovDir[x][y] = old_move_dir;
6893
6894     MovDelay[x][y] = 0;
6895   }
6896   else if (element == EL_DRAGON)
6897   {
6898     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6899     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6900     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6901     int rnd_value = 24;
6902     int rnd = RND(rnd_value);
6903
6904     if (can_move_on && rnd > rnd_value / 8)
6905       MovDir[x][y] = old_move_dir;
6906     else if (can_turn_left && can_turn_right)
6907       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6908     else if (can_turn_left && rnd > rnd_value / 8)
6909       MovDir[x][y] = left_dir;
6910     else if (can_turn_right && rnd > rnd_value / 8)
6911       MovDir[x][y] = right_dir;
6912     else
6913       MovDir[x][y] = back_dir;
6914
6915     xx = x + move_xy[MovDir[x][y]].dx;
6916     yy = y + move_xy[MovDir[x][y]].dy;
6917
6918     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6919       MovDir[x][y] = old_move_dir;
6920
6921     MovDelay[x][y] = 0;
6922   }
6923   else if (element == EL_MOLE)
6924   {
6925     boolean can_move_on =
6926       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6927                             IS_AMOEBOID(Tile[move_x][move_y]) ||
6928                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
6929     if (!can_move_on)
6930     {
6931       boolean can_turn_left =
6932         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6933                               IS_AMOEBOID(Tile[left_x][left_y])));
6934
6935       boolean can_turn_right =
6936         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6937                               IS_AMOEBOID(Tile[right_x][right_y])));
6938
6939       if (can_turn_left && can_turn_right)
6940         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6941       else if (can_turn_left)
6942         MovDir[x][y] = left_dir;
6943       else
6944         MovDir[x][y] = right_dir;
6945     }
6946
6947     if (MovDir[x][y] != old_move_dir)
6948       MovDelay[x][y] = 9;
6949   }
6950   else if (element == EL_BALLOON)
6951   {
6952     MovDir[x][y] = game.wind_direction;
6953     MovDelay[x][y] = 0;
6954   }
6955   else if (element == EL_SPRING)
6956   {
6957     if (MovDir[x][y] & MV_HORIZONTAL)
6958     {
6959       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6960           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6961       {
6962         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6963         ResetGfxAnimation(move_x, move_y);
6964         TEST_DrawLevelField(move_x, move_y);
6965
6966         MovDir[x][y] = back_dir;
6967       }
6968       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6969                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6970         MovDir[x][y] = MV_NONE;
6971     }
6972
6973     MovDelay[x][y] = 0;
6974   }
6975   else if (element == EL_ROBOT ||
6976            element == EL_SATELLITE ||
6977            element == EL_PENGUIN ||
6978            element == EL_EMC_ANDROID)
6979   {
6980     int attr_x = -1, attr_y = -1;
6981
6982     if (game.all_players_gone)
6983     {
6984       attr_x = game.exit_x;
6985       attr_y = game.exit_y;
6986     }
6987     else
6988     {
6989       int i;
6990
6991       for (i = 0; i < MAX_PLAYERS; i++)
6992       {
6993         struct PlayerInfo *player = &stored_player[i];
6994         int jx = player->jx, jy = player->jy;
6995
6996         if (!player->active)
6997           continue;
6998
6999         if (attr_x == -1 ||
7000             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7001         {
7002           attr_x = jx;
7003           attr_y = jy;
7004         }
7005       }
7006     }
7007
7008     if (element == EL_ROBOT &&
7009         game.robot_wheel_x >= 0 &&
7010         game.robot_wheel_y >= 0 &&
7011         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7012          game.engine_version < VERSION_IDENT(3,1,0,0)))
7013     {
7014       attr_x = game.robot_wheel_x;
7015       attr_y = game.robot_wheel_y;
7016     }
7017
7018     if (element == EL_PENGUIN)
7019     {
7020       int i;
7021       static int xy[4][2] =
7022       {
7023         { 0, -1 },
7024         { -1, 0 },
7025         { +1, 0 },
7026         { 0, +1 }
7027       };
7028
7029       for (i = 0; i < NUM_DIRECTIONS; i++)
7030       {
7031         int ex = x + xy[i][0];
7032         int ey = y + xy[i][1];
7033
7034         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7035                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7036                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7037                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7038         {
7039           attr_x = ex;
7040           attr_y = ey;
7041           break;
7042         }
7043       }
7044     }
7045
7046     MovDir[x][y] = MV_NONE;
7047     if (attr_x < x)
7048       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7049     else if (attr_x > x)
7050       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7051     if (attr_y < y)
7052       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7053     else if (attr_y > y)
7054       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7055
7056     if (element == EL_ROBOT)
7057     {
7058       int newx, newy;
7059
7060       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7061         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7062       Moving2Blocked(x, y, &newx, &newy);
7063
7064       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7065         MovDelay[x][y] = 8 + 8 * !RND(3);
7066       else
7067         MovDelay[x][y] = 16;
7068     }
7069     else if (element == EL_PENGUIN)
7070     {
7071       int newx, newy;
7072
7073       MovDelay[x][y] = 1;
7074
7075       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7076       {
7077         boolean first_horiz = RND(2);
7078         int new_move_dir = MovDir[x][y];
7079
7080         MovDir[x][y] =
7081           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7082         Moving2Blocked(x, y, &newx, &newy);
7083
7084         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7085           return;
7086
7087         MovDir[x][y] =
7088           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7089         Moving2Blocked(x, y, &newx, &newy);
7090
7091         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7092           return;
7093
7094         MovDir[x][y] = old_move_dir;
7095         return;
7096       }
7097     }
7098     else if (element == EL_SATELLITE)
7099     {
7100       int newx, newy;
7101
7102       MovDelay[x][y] = 1;
7103
7104       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7105       {
7106         boolean first_horiz = RND(2);
7107         int new_move_dir = MovDir[x][y];
7108
7109         MovDir[x][y] =
7110           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7111         Moving2Blocked(x, y, &newx, &newy);
7112
7113         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7114           return;
7115
7116         MovDir[x][y] =
7117           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7118         Moving2Blocked(x, y, &newx, &newy);
7119
7120         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7121           return;
7122
7123         MovDir[x][y] = old_move_dir;
7124         return;
7125       }
7126     }
7127     else if (element == EL_EMC_ANDROID)
7128     {
7129       static int check_pos[16] =
7130       {
7131         -1,             //  0 => (invalid)
7132         7,              //  1 => MV_LEFT
7133         3,              //  2 => MV_RIGHT
7134         -1,             //  3 => (invalid)
7135         1,              //  4 =>            MV_UP
7136         0,              //  5 => MV_LEFT  | MV_UP
7137         2,              //  6 => MV_RIGHT | MV_UP
7138         -1,             //  7 => (invalid)
7139         5,              //  8 =>            MV_DOWN
7140         6,              //  9 => MV_LEFT  | MV_DOWN
7141         4,              // 10 => MV_RIGHT | MV_DOWN
7142         -1,             // 11 => (invalid)
7143         -1,             // 12 => (invalid)
7144         -1,             // 13 => (invalid)
7145         -1,             // 14 => (invalid)
7146         -1,             // 15 => (invalid)
7147       };
7148       static struct
7149       {
7150         int dx, dy;
7151         int dir;
7152       } check_xy[8] =
7153       {
7154         { -1, -1,       MV_LEFT  | MV_UP   },
7155         {  0, -1,                  MV_UP   },
7156         { +1, -1,       MV_RIGHT | MV_UP   },
7157         { +1,  0,       MV_RIGHT           },
7158         { +1, +1,       MV_RIGHT | MV_DOWN },
7159         {  0, +1,                  MV_DOWN },
7160         { -1, +1,       MV_LEFT  | MV_DOWN },
7161         { -1,  0,       MV_LEFT            },
7162       };
7163       int start_pos, check_order;
7164       boolean can_clone = FALSE;
7165       int i;
7166
7167       // check if there is any free field around current position
7168       for (i = 0; i < 8; i++)
7169       {
7170         int newx = x + check_xy[i].dx;
7171         int newy = y + check_xy[i].dy;
7172
7173         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7174         {
7175           can_clone = TRUE;
7176
7177           break;
7178         }
7179       }
7180
7181       if (can_clone)            // randomly find an element to clone
7182       {
7183         can_clone = FALSE;
7184
7185         start_pos = check_pos[RND(8)];
7186         check_order = (RND(2) ? -1 : +1);
7187
7188         for (i = 0; i < 8; i++)
7189         {
7190           int pos_raw = start_pos + i * check_order;
7191           int pos = (pos_raw + 8) % 8;
7192           int newx = x + check_xy[pos].dx;
7193           int newy = y + check_xy[pos].dy;
7194
7195           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7196           {
7197             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7198             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7199
7200             Store[x][y] = Tile[newx][newy];
7201
7202             can_clone = TRUE;
7203
7204             break;
7205           }
7206         }
7207       }
7208
7209       if (can_clone)            // randomly find a direction to move
7210       {
7211         can_clone = FALSE;
7212
7213         start_pos = check_pos[RND(8)];
7214         check_order = (RND(2) ? -1 : +1);
7215
7216         for (i = 0; i < 8; i++)
7217         {
7218           int pos_raw = start_pos + i * check_order;
7219           int pos = (pos_raw + 8) % 8;
7220           int newx = x + check_xy[pos].dx;
7221           int newy = y + check_xy[pos].dy;
7222           int new_move_dir = check_xy[pos].dir;
7223
7224           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7225           {
7226             MovDir[x][y] = new_move_dir;
7227             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7228
7229             can_clone = TRUE;
7230
7231             break;
7232           }
7233         }
7234       }
7235
7236       if (can_clone)            // cloning and moving successful
7237         return;
7238
7239       // cannot clone -- try to move towards player
7240
7241       start_pos = check_pos[MovDir[x][y] & 0x0f];
7242       check_order = (RND(2) ? -1 : +1);
7243
7244       for (i = 0; i < 3; i++)
7245       {
7246         // first check start_pos, then previous/next or (next/previous) pos
7247         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7248         int pos = (pos_raw + 8) % 8;
7249         int newx = x + check_xy[pos].dx;
7250         int newy = y + check_xy[pos].dy;
7251         int new_move_dir = check_xy[pos].dir;
7252
7253         if (IS_PLAYER(newx, newy))
7254           break;
7255
7256         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7257         {
7258           MovDir[x][y] = new_move_dir;
7259           MovDelay[x][y] = level.android_move_time * 8 + 1;
7260
7261           break;
7262         }
7263       }
7264     }
7265   }
7266   else if (move_pattern == MV_TURNING_LEFT ||
7267            move_pattern == MV_TURNING_RIGHT ||
7268            move_pattern == MV_TURNING_LEFT_RIGHT ||
7269            move_pattern == MV_TURNING_RIGHT_LEFT ||
7270            move_pattern == MV_TURNING_RANDOM ||
7271            move_pattern == MV_ALL_DIRECTIONS)
7272   {
7273     boolean can_turn_left =
7274       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7275     boolean can_turn_right =
7276       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7277
7278     if (element_info[element].move_stepsize == 0)       // "not moving"
7279       return;
7280
7281     if (move_pattern == MV_TURNING_LEFT)
7282       MovDir[x][y] = left_dir;
7283     else if (move_pattern == MV_TURNING_RIGHT)
7284       MovDir[x][y] = right_dir;
7285     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7286       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7287     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7288       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7289     else if (move_pattern == MV_TURNING_RANDOM)
7290       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7291                       can_turn_right && !can_turn_left ? right_dir :
7292                       RND(2) ? left_dir : right_dir);
7293     else if (can_turn_left && can_turn_right)
7294       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7295     else if (can_turn_left)
7296       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7297     else if (can_turn_right)
7298       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7299     else
7300       MovDir[x][y] = back_dir;
7301
7302     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7303   }
7304   else if (move_pattern == MV_HORIZONTAL ||
7305            move_pattern == MV_VERTICAL)
7306   {
7307     if (move_pattern & old_move_dir)
7308       MovDir[x][y] = back_dir;
7309     else if (move_pattern == MV_HORIZONTAL)
7310       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7311     else if (move_pattern == MV_VERTICAL)
7312       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7313
7314     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7315   }
7316   else if (move_pattern & MV_ANY_DIRECTION)
7317   {
7318     MovDir[x][y] = move_pattern;
7319     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7320   }
7321   else if (move_pattern & MV_WIND_DIRECTION)
7322   {
7323     MovDir[x][y] = game.wind_direction;
7324     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7325   }
7326   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7327   {
7328     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7329       MovDir[x][y] = left_dir;
7330     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7331       MovDir[x][y] = right_dir;
7332
7333     if (MovDir[x][y] != old_move_dir)
7334       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7335   }
7336   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7337   {
7338     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7339       MovDir[x][y] = right_dir;
7340     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7341       MovDir[x][y] = left_dir;
7342
7343     if (MovDir[x][y] != old_move_dir)
7344       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7345   }
7346   else if (move_pattern == MV_TOWARDS_PLAYER ||
7347            move_pattern == MV_AWAY_FROM_PLAYER)
7348   {
7349     int attr_x = -1, attr_y = -1;
7350     int newx, newy;
7351     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7352
7353     if (game.all_players_gone)
7354     {
7355       attr_x = game.exit_x;
7356       attr_y = game.exit_y;
7357     }
7358     else
7359     {
7360       int i;
7361
7362       for (i = 0; i < MAX_PLAYERS; i++)
7363       {
7364         struct PlayerInfo *player = &stored_player[i];
7365         int jx = player->jx, jy = player->jy;
7366
7367         if (!player->active)
7368           continue;
7369
7370         if (attr_x == -1 ||
7371             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7372         {
7373           attr_x = jx;
7374           attr_y = jy;
7375         }
7376       }
7377     }
7378
7379     MovDir[x][y] = MV_NONE;
7380     if (attr_x < x)
7381       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7382     else if (attr_x > x)
7383       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7384     if (attr_y < y)
7385       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7386     else if (attr_y > y)
7387       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7388
7389     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7390
7391     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7392     {
7393       boolean first_horiz = RND(2);
7394       int new_move_dir = MovDir[x][y];
7395
7396       if (element_info[element].move_stepsize == 0)     // "not moving"
7397       {
7398         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7399         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7400
7401         return;
7402       }
7403
7404       MovDir[x][y] =
7405         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7406       Moving2Blocked(x, y, &newx, &newy);
7407
7408       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7409         return;
7410
7411       MovDir[x][y] =
7412         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7413       Moving2Blocked(x, y, &newx, &newy);
7414
7415       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7416         return;
7417
7418       MovDir[x][y] = old_move_dir;
7419     }
7420   }
7421   else if (move_pattern == MV_WHEN_PUSHED ||
7422            move_pattern == MV_WHEN_DROPPED)
7423   {
7424     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7425       MovDir[x][y] = MV_NONE;
7426
7427     MovDelay[x][y] = 0;
7428   }
7429   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7430   {
7431     static int test_xy[7][2] =
7432     {
7433       { 0, -1 },
7434       { -1, 0 },
7435       { +1, 0 },
7436       { 0, +1 },
7437       { 0, -1 },
7438       { -1, 0 },
7439       { +1, 0 },
7440     };
7441     static int test_dir[7] =
7442     {
7443       MV_UP,
7444       MV_LEFT,
7445       MV_RIGHT,
7446       MV_DOWN,
7447       MV_UP,
7448       MV_LEFT,
7449       MV_RIGHT,
7450     };
7451     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7452     int move_preference = -1000000;     // start with very low preference
7453     int new_move_dir = MV_NONE;
7454     int start_test = RND(4);
7455     int i;
7456
7457     for (i = 0; i < NUM_DIRECTIONS; i++)
7458     {
7459       int move_dir = test_dir[start_test + i];
7460       int move_dir_preference;
7461
7462       xx = x + test_xy[start_test + i][0];
7463       yy = y + test_xy[start_test + i][1];
7464
7465       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7466           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7467       {
7468         new_move_dir = move_dir;
7469
7470         break;
7471       }
7472
7473       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7474         continue;
7475
7476       move_dir_preference = -1 * RunnerVisit[xx][yy];
7477       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7478         move_dir_preference = PlayerVisit[xx][yy];
7479
7480       if (move_dir_preference > move_preference)
7481       {
7482         // prefer field that has not been visited for the longest time
7483         move_preference = move_dir_preference;
7484         new_move_dir = move_dir;
7485       }
7486       else if (move_dir_preference == move_preference &&
7487                move_dir == old_move_dir)
7488       {
7489         // prefer last direction when all directions are preferred equally
7490         move_preference = move_dir_preference;
7491         new_move_dir = move_dir;
7492       }
7493     }
7494
7495     MovDir[x][y] = new_move_dir;
7496     if (old_move_dir != new_move_dir)
7497       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7498   }
7499 }
7500
7501 static void TurnRound(int x, int y)
7502 {
7503   int direction = MovDir[x][y];
7504
7505   TurnRoundExt(x, y);
7506
7507   GfxDir[x][y] = MovDir[x][y];
7508
7509   if (direction != MovDir[x][y])
7510     GfxFrame[x][y] = 0;
7511
7512   if (MovDelay[x][y])
7513     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7514
7515   ResetGfxFrame(x, y);
7516 }
7517
7518 static boolean JustBeingPushed(int x, int y)
7519 {
7520   int i;
7521
7522   for (i = 0; i < MAX_PLAYERS; i++)
7523   {
7524     struct PlayerInfo *player = &stored_player[i];
7525
7526     if (player->active && player->is_pushing && player->MovPos)
7527     {
7528       int next_jx = player->jx + (player->jx - player->last_jx);
7529       int next_jy = player->jy + (player->jy - player->last_jy);
7530
7531       if (x == next_jx && y == next_jy)
7532         return TRUE;
7533     }
7534   }
7535
7536   return FALSE;
7537 }
7538
7539 static void StartMoving(int x, int y)
7540 {
7541   boolean started_moving = FALSE;       // some elements can fall _and_ move
7542   int element = Tile[x][y];
7543
7544   if (Stop[x][y])
7545     return;
7546
7547   if (MovDelay[x][y] == 0)
7548     GfxAction[x][y] = ACTION_DEFAULT;
7549
7550   if (CAN_FALL(element) && y < lev_fieldy - 1)
7551   {
7552     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7553         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7554       if (JustBeingPushed(x, y))
7555         return;
7556
7557     if (element == EL_QUICKSAND_FULL)
7558     {
7559       if (IS_FREE(x, y + 1))
7560       {
7561         InitMovingField(x, y, MV_DOWN);
7562         started_moving = TRUE;
7563
7564         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7565 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7566         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7567           Store[x][y] = EL_ROCK;
7568 #else
7569         Store[x][y] = EL_ROCK;
7570 #endif
7571
7572         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7573       }
7574       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7575       {
7576         if (!MovDelay[x][y])
7577         {
7578           MovDelay[x][y] = TILEY + 1;
7579
7580           ResetGfxAnimation(x, y);
7581           ResetGfxAnimation(x, y + 1);
7582         }
7583
7584         if (MovDelay[x][y])
7585         {
7586           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7587           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7588
7589           MovDelay[x][y]--;
7590           if (MovDelay[x][y])
7591             return;
7592         }
7593
7594         Tile[x][y] = EL_QUICKSAND_EMPTY;
7595         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7596         Store[x][y + 1] = Store[x][y];
7597         Store[x][y] = 0;
7598
7599         PlayLevelSoundAction(x, y, ACTION_FILLING);
7600       }
7601       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7602       {
7603         if (!MovDelay[x][y])
7604         {
7605           MovDelay[x][y] = TILEY + 1;
7606
7607           ResetGfxAnimation(x, y);
7608           ResetGfxAnimation(x, y + 1);
7609         }
7610
7611         if (MovDelay[x][y])
7612         {
7613           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7614           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7615
7616           MovDelay[x][y]--;
7617           if (MovDelay[x][y])
7618             return;
7619         }
7620
7621         Tile[x][y] = EL_QUICKSAND_EMPTY;
7622         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7623         Store[x][y + 1] = Store[x][y];
7624         Store[x][y] = 0;
7625
7626         PlayLevelSoundAction(x, y, ACTION_FILLING);
7627       }
7628     }
7629     else if (element == EL_QUICKSAND_FAST_FULL)
7630     {
7631       if (IS_FREE(x, y + 1))
7632       {
7633         InitMovingField(x, y, MV_DOWN);
7634         started_moving = TRUE;
7635
7636         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7637 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7638         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7639           Store[x][y] = EL_ROCK;
7640 #else
7641         Store[x][y] = EL_ROCK;
7642 #endif
7643
7644         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7645       }
7646       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7647       {
7648         if (!MovDelay[x][y])
7649         {
7650           MovDelay[x][y] = TILEY + 1;
7651
7652           ResetGfxAnimation(x, y);
7653           ResetGfxAnimation(x, y + 1);
7654         }
7655
7656         if (MovDelay[x][y])
7657         {
7658           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7659           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7660
7661           MovDelay[x][y]--;
7662           if (MovDelay[x][y])
7663             return;
7664         }
7665
7666         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7667         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7668         Store[x][y + 1] = Store[x][y];
7669         Store[x][y] = 0;
7670
7671         PlayLevelSoundAction(x, y, ACTION_FILLING);
7672       }
7673       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7674       {
7675         if (!MovDelay[x][y])
7676         {
7677           MovDelay[x][y] = TILEY + 1;
7678
7679           ResetGfxAnimation(x, y);
7680           ResetGfxAnimation(x, y + 1);
7681         }
7682
7683         if (MovDelay[x][y])
7684         {
7685           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7686           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7687
7688           MovDelay[x][y]--;
7689           if (MovDelay[x][y])
7690             return;
7691         }
7692
7693         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7694         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7695         Store[x][y + 1] = Store[x][y];
7696         Store[x][y] = 0;
7697
7698         PlayLevelSoundAction(x, y, ACTION_FILLING);
7699       }
7700     }
7701     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7702              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7703     {
7704       InitMovingField(x, y, MV_DOWN);
7705       started_moving = TRUE;
7706
7707       Tile[x][y] = EL_QUICKSAND_FILLING;
7708       Store[x][y] = element;
7709
7710       PlayLevelSoundAction(x, y, ACTION_FILLING);
7711     }
7712     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7713              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7714     {
7715       InitMovingField(x, y, MV_DOWN);
7716       started_moving = TRUE;
7717
7718       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7719       Store[x][y] = element;
7720
7721       PlayLevelSoundAction(x, y, ACTION_FILLING);
7722     }
7723     else if (element == EL_MAGIC_WALL_FULL)
7724     {
7725       if (IS_FREE(x, y + 1))
7726       {
7727         InitMovingField(x, y, MV_DOWN);
7728         started_moving = TRUE;
7729
7730         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7731         Store[x][y] = EL_CHANGED(Store[x][y]);
7732       }
7733       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7734       {
7735         if (!MovDelay[x][y])
7736           MovDelay[x][y] = TILEY / 4 + 1;
7737
7738         if (MovDelay[x][y])
7739         {
7740           MovDelay[x][y]--;
7741           if (MovDelay[x][y])
7742             return;
7743         }
7744
7745         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7746         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7747         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7748         Store[x][y] = 0;
7749       }
7750     }
7751     else if (element == EL_BD_MAGIC_WALL_FULL)
7752     {
7753       if (IS_FREE(x, y + 1))
7754       {
7755         InitMovingField(x, y, MV_DOWN);
7756         started_moving = TRUE;
7757
7758         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7759         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7760       }
7761       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7762       {
7763         if (!MovDelay[x][y])
7764           MovDelay[x][y] = TILEY / 4 + 1;
7765
7766         if (MovDelay[x][y])
7767         {
7768           MovDelay[x][y]--;
7769           if (MovDelay[x][y])
7770             return;
7771         }
7772
7773         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7774         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7775         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7776         Store[x][y] = 0;
7777       }
7778     }
7779     else if (element == EL_DC_MAGIC_WALL_FULL)
7780     {
7781       if (IS_FREE(x, y + 1))
7782       {
7783         InitMovingField(x, y, MV_DOWN);
7784         started_moving = TRUE;
7785
7786         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7787         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7788       }
7789       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7790       {
7791         if (!MovDelay[x][y])
7792           MovDelay[x][y] = TILEY / 4 + 1;
7793
7794         if (MovDelay[x][y])
7795         {
7796           MovDelay[x][y]--;
7797           if (MovDelay[x][y])
7798             return;
7799         }
7800
7801         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7802         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7803         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7804         Store[x][y] = 0;
7805       }
7806     }
7807     else if ((CAN_PASS_MAGIC_WALL(element) &&
7808               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7809                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7810              (CAN_PASS_DC_MAGIC_WALL(element) &&
7811               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7812
7813     {
7814       InitMovingField(x, y, MV_DOWN);
7815       started_moving = TRUE;
7816
7817       Tile[x][y] =
7818         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7819          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7820          EL_DC_MAGIC_WALL_FILLING);
7821       Store[x][y] = element;
7822     }
7823     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
7824     {
7825       SplashAcid(x, y + 1);
7826
7827       InitMovingField(x, y, MV_DOWN);
7828       started_moving = TRUE;
7829
7830       Store[x][y] = EL_ACID;
7831     }
7832     else if (
7833              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7834               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7835              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7836               CAN_FALL(element) && WasJustFalling[x][y] &&
7837               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7838
7839              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7840               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7841               (Tile[x][y + 1] == EL_BLOCKED)))
7842     {
7843       /* this is needed for a special case not covered by calling "Impact()"
7844          from "ContinueMoving()": if an element moves to a tile directly below
7845          another element which was just falling on that tile (which was empty
7846          in the previous frame), the falling element above would just stop
7847          instead of smashing the element below (in previous version, the above
7848          element was just checked for "moving" instead of "falling", resulting
7849          in incorrect smashes caused by horizontal movement of the above
7850          element; also, the case of the player being the element to smash was
7851          simply not covered here... :-/ ) */
7852
7853       CheckCollision[x][y] = 0;
7854       CheckImpact[x][y] = 0;
7855
7856       Impact(x, y);
7857     }
7858     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7859     {
7860       if (MovDir[x][y] == MV_NONE)
7861       {
7862         InitMovingField(x, y, MV_DOWN);
7863         started_moving = TRUE;
7864       }
7865     }
7866     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
7867     {
7868       if (WasJustFalling[x][y]) // prevent animation from being restarted
7869         MovDir[x][y] = MV_DOWN;
7870
7871       InitMovingField(x, y, MV_DOWN);
7872       started_moving = TRUE;
7873     }
7874     else if (element == EL_AMOEBA_DROP)
7875     {
7876       Tile[x][y] = EL_AMOEBA_GROWING;
7877       Store[x][y] = EL_AMOEBA_WET;
7878     }
7879     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7880               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
7881              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7882              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7883     {
7884       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7885                                 (IS_FREE(x - 1, y + 1) ||
7886                                  Tile[x - 1][y + 1] == EL_ACID));
7887       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7888                                 (IS_FREE(x + 1, y + 1) ||
7889                                  Tile[x + 1][y + 1] == EL_ACID));
7890       boolean can_fall_any  = (can_fall_left || can_fall_right);
7891       boolean can_fall_both = (can_fall_left && can_fall_right);
7892       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
7893
7894       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7895       {
7896         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7897           can_fall_right = FALSE;
7898         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7899           can_fall_left = FALSE;
7900         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7901           can_fall_right = FALSE;
7902         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7903           can_fall_left = FALSE;
7904
7905         can_fall_any  = (can_fall_left || can_fall_right);
7906         can_fall_both = FALSE;
7907       }
7908
7909       if (can_fall_both)
7910       {
7911         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7912           can_fall_right = FALSE;       // slip down on left side
7913         else
7914           can_fall_left = !(can_fall_right = RND(2));
7915
7916         can_fall_both = FALSE;
7917       }
7918
7919       if (can_fall_any)
7920       {
7921         // if not determined otherwise, prefer left side for slipping down
7922         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7923         started_moving = TRUE;
7924       }
7925     }
7926     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
7927     {
7928       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7929       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7930       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
7931       int belt_dir = game.belt_dir[belt_nr];
7932
7933       if ((belt_dir == MV_LEFT  && left_is_free) ||
7934           (belt_dir == MV_RIGHT && right_is_free))
7935       {
7936         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7937
7938         InitMovingField(x, y, belt_dir);
7939         started_moving = TRUE;
7940
7941         Pushed[x][y] = TRUE;
7942         Pushed[nextx][y] = TRUE;
7943
7944         GfxAction[x][y] = ACTION_DEFAULT;
7945       }
7946       else
7947       {
7948         MovDir[x][y] = 0;       // if element was moving, stop it
7949       }
7950     }
7951   }
7952
7953   // not "else if" because of elements that can fall and move (EL_SPRING)
7954   if (CAN_MOVE(element) && !started_moving)
7955   {
7956     int move_pattern = element_info[element].move_pattern;
7957     int newx, newy;
7958
7959     Moving2Blocked(x, y, &newx, &newy);
7960
7961     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7962       return;
7963
7964     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7965         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7966     {
7967       WasJustMoving[x][y] = 0;
7968       CheckCollision[x][y] = 0;
7969
7970       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7971
7972       if (Tile[x][y] != element)        // element has changed
7973         return;
7974     }
7975
7976     if (!MovDelay[x][y])        // start new movement phase
7977     {
7978       // all objects that can change their move direction after each step
7979       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
7980
7981       if (element != EL_YAMYAM &&
7982           element != EL_DARK_YAMYAM &&
7983           element != EL_PACMAN &&
7984           !(move_pattern & MV_ANY_DIRECTION) &&
7985           move_pattern != MV_TURNING_LEFT &&
7986           move_pattern != MV_TURNING_RIGHT &&
7987           move_pattern != MV_TURNING_LEFT_RIGHT &&
7988           move_pattern != MV_TURNING_RIGHT_LEFT &&
7989           move_pattern != MV_TURNING_RANDOM)
7990       {
7991         TurnRound(x, y);
7992
7993         if (MovDelay[x][y] && (element == EL_BUG ||
7994                                element == EL_SPACESHIP ||
7995                                element == EL_SP_SNIKSNAK ||
7996                                element == EL_SP_ELECTRON ||
7997                                element == EL_MOLE))
7998           TEST_DrawLevelField(x, y);
7999       }
8000     }
8001
8002     if (MovDelay[x][y])         // wait some time before next movement
8003     {
8004       MovDelay[x][y]--;
8005
8006       if (element == EL_ROBOT ||
8007           element == EL_YAMYAM ||
8008           element == EL_DARK_YAMYAM)
8009       {
8010         DrawLevelElementAnimationIfNeeded(x, y, element);
8011         PlayLevelSoundAction(x, y, ACTION_WAITING);
8012       }
8013       else if (element == EL_SP_ELECTRON)
8014         DrawLevelElementAnimationIfNeeded(x, y, element);
8015       else if (element == EL_DRAGON)
8016       {
8017         int i;
8018         int dir = MovDir[x][y];
8019         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8020         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8021         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8022                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8023                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8024                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8025         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8026
8027         GfxAction[x][y] = ACTION_ATTACKING;
8028
8029         if (IS_PLAYER(x, y))
8030           DrawPlayerField(x, y);
8031         else
8032           TEST_DrawLevelField(x, y);
8033
8034         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8035
8036         for (i = 1; i <= 3; i++)
8037         {
8038           int xx = x + i * dx;
8039           int yy = y + i * dy;
8040           int sx = SCREENX(xx);
8041           int sy = SCREENY(yy);
8042           int flame_graphic = graphic + (i - 1);
8043
8044           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8045             break;
8046
8047           if (MovDelay[x][y])
8048           {
8049             int flamed = MovingOrBlocked2Element(xx, yy);
8050
8051             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8052               Bang(xx, yy);
8053             else
8054               RemoveMovingField(xx, yy);
8055
8056             ChangeDelay[xx][yy] = 0;
8057
8058             Tile[xx][yy] = EL_FLAMES;
8059
8060             if (IN_SCR_FIELD(sx, sy))
8061             {
8062               TEST_DrawLevelFieldCrumbled(xx, yy);
8063               DrawGraphic(sx, sy, flame_graphic, frame);
8064             }
8065           }
8066           else
8067           {
8068             if (Tile[xx][yy] == EL_FLAMES)
8069               Tile[xx][yy] = EL_EMPTY;
8070             TEST_DrawLevelField(xx, yy);
8071           }
8072         }
8073       }
8074
8075       if (MovDelay[x][y])       // element still has to wait some time
8076       {
8077         PlayLevelSoundAction(x, y, ACTION_WAITING);
8078
8079         return;
8080       }
8081     }
8082
8083     // now make next step
8084
8085     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8086
8087     if (DONT_COLLIDE_WITH(element) &&
8088         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8089         !PLAYER_ENEMY_PROTECTED(newx, newy))
8090     {
8091       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8092
8093       return;
8094     }
8095
8096     else if (CAN_MOVE_INTO_ACID(element) &&
8097              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8098              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8099              (MovDir[x][y] == MV_DOWN ||
8100               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8101     {
8102       SplashAcid(newx, newy);
8103       Store[x][y] = EL_ACID;
8104     }
8105     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8106     {
8107       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8108           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8109           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8110           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8111       {
8112         RemoveField(x, y);
8113         TEST_DrawLevelField(x, y);
8114
8115         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8116         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8117           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8118
8119         game.friends_still_needed--;
8120         if (!game.friends_still_needed &&
8121             !game.GameOver &&
8122             game.all_players_gone)
8123           LevelSolved();
8124
8125         return;
8126       }
8127       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8128       {
8129         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8130           TEST_DrawLevelField(newx, newy);
8131         else
8132           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8133       }
8134       else if (!IS_FREE(newx, newy))
8135       {
8136         GfxAction[x][y] = ACTION_WAITING;
8137
8138         if (IS_PLAYER(x, y))
8139           DrawPlayerField(x, y);
8140         else
8141           TEST_DrawLevelField(x, y);
8142
8143         return;
8144       }
8145     }
8146     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8147     {
8148       if (IS_FOOD_PIG(Tile[newx][newy]))
8149       {
8150         if (IS_MOVING(newx, newy))
8151           RemoveMovingField(newx, newy);
8152         else
8153         {
8154           Tile[newx][newy] = EL_EMPTY;
8155           TEST_DrawLevelField(newx, newy);
8156         }
8157
8158         PlayLevelSound(x, y, SND_PIG_DIGGING);
8159       }
8160       else if (!IS_FREE(newx, newy))
8161       {
8162         if (IS_PLAYER(x, y))
8163           DrawPlayerField(x, y);
8164         else
8165           TEST_DrawLevelField(x, y);
8166
8167         return;
8168       }
8169     }
8170     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8171     {
8172       if (Store[x][y] != EL_EMPTY)
8173       {
8174         boolean can_clone = FALSE;
8175         int xx, yy;
8176
8177         // check if element to clone is still there
8178         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8179         {
8180           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8181           {
8182             can_clone = TRUE;
8183
8184             break;
8185           }
8186         }
8187
8188         // cannot clone or target field not free anymore -- do not clone
8189         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8190           Store[x][y] = EL_EMPTY;
8191       }
8192
8193       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8194       {
8195         if (IS_MV_DIAGONAL(MovDir[x][y]))
8196         {
8197           int diagonal_move_dir = MovDir[x][y];
8198           int stored = Store[x][y];
8199           int change_delay = 8;
8200           int graphic;
8201
8202           // android is moving diagonally
8203
8204           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8205
8206           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8207           GfxElement[x][y] = EL_EMC_ANDROID;
8208           GfxAction[x][y] = ACTION_SHRINKING;
8209           GfxDir[x][y] = diagonal_move_dir;
8210           ChangeDelay[x][y] = change_delay;
8211
8212           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8213                                    GfxDir[x][y]);
8214
8215           DrawLevelGraphicAnimation(x, y, graphic);
8216           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8217
8218           if (Tile[newx][newy] == EL_ACID)
8219           {
8220             SplashAcid(newx, newy);
8221
8222             return;
8223           }
8224
8225           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8226
8227           Store[newx][newy] = EL_EMC_ANDROID;
8228           GfxElement[newx][newy] = EL_EMC_ANDROID;
8229           GfxAction[newx][newy] = ACTION_GROWING;
8230           GfxDir[newx][newy] = diagonal_move_dir;
8231           ChangeDelay[newx][newy] = change_delay;
8232
8233           graphic = el_act_dir2img(GfxElement[newx][newy],
8234                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8235
8236           DrawLevelGraphicAnimation(newx, newy, graphic);
8237           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8238
8239           return;
8240         }
8241         else
8242         {
8243           Tile[newx][newy] = EL_EMPTY;
8244           TEST_DrawLevelField(newx, newy);
8245
8246           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8247         }
8248       }
8249       else if (!IS_FREE(newx, newy))
8250       {
8251         return;
8252       }
8253     }
8254     else if (IS_CUSTOM_ELEMENT(element) &&
8255              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8256     {
8257       if (!DigFieldByCE(newx, newy, element))
8258         return;
8259
8260       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8261       {
8262         RunnerVisit[x][y] = FrameCounter;
8263         PlayerVisit[x][y] /= 8;         // expire player visit path
8264       }
8265     }
8266     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8267     {
8268       if (!IS_FREE(newx, newy))
8269       {
8270         if (IS_PLAYER(x, y))
8271           DrawPlayerField(x, y);
8272         else
8273           TEST_DrawLevelField(x, y);
8274
8275         return;
8276       }
8277       else
8278       {
8279         boolean wanna_flame = !RND(10);
8280         int dx = newx - x, dy = newy - y;
8281         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8282         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8283         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8284                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8285         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8286                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8287
8288         if ((wanna_flame ||
8289              IS_CLASSIC_ENEMY(element1) ||
8290              IS_CLASSIC_ENEMY(element2)) &&
8291             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8292             element1 != EL_FLAMES && element2 != EL_FLAMES)
8293         {
8294           ResetGfxAnimation(x, y);
8295           GfxAction[x][y] = ACTION_ATTACKING;
8296
8297           if (IS_PLAYER(x, y))
8298             DrawPlayerField(x, y);
8299           else
8300             TEST_DrawLevelField(x, y);
8301
8302           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8303
8304           MovDelay[x][y] = 50;
8305
8306           Tile[newx][newy] = EL_FLAMES;
8307           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8308             Tile[newx1][newy1] = EL_FLAMES;
8309           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8310             Tile[newx2][newy2] = EL_FLAMES;
8311
8312           return;
8313         }
8314       }
8315     }
8316     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8317              Tile[newx][newy] == EL_DIAMOND)
8318     {
8319       if (IS_MOVING(newx, newy))
8320         RemoveMovingField(newx, newy);
8321       else
8322       {
8323         Tile[newx][newy] = EL_EMPTY;
8324         TEST_DrawLevelField(newx, newy);
8325       }
8326
8327       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8328     }
8329     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8330              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8331     {
8332       if (AmoebaNr[newx][newy])
8333       {
8334         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8335         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8336             Tile[newx][newy] == EL_BD_AMOEBA)
8337           AmoebaCnt[AmoebaNr[newx][newy]]--;
8338       }
8339
8340       if (IS_MOVING(newx, newy))
8341       {
8342         RemoveMovingField(newx, newy);
8343       }
8344       else
8345       {
8346         Tile[newx][newy] = EL_EMPTY;
8347         TEST_DrawLevelField(newx, newy);
8348       }
8349
8350       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8351     }
8352     else if ((element == EL_PACMAN || element == EL_MOLE)
8353              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8354     {
8355       if (AmoebaNr[newx][newy])
8356       {
8357         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8358         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8359             Tile[newx][newy] == EL_BD_AMOEBA)
8360           AmoebaCnt[AmoebaNr[newx][newy]]--;
8361       }
8362
8363       if (element == EL_MOLE)
8364       {
8365         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8366         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8367
8368         ResetGfxAnimation(x, y);
8369         GfxAction[x][y] = ACTION_DIGGING;
8370         TEST_DrawLevelField(x, y);
8371
8372         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8373
8374         return;                         // wait for shrinking amoeba
8375       }
8376       else      // element == EL_PACMAN
8377       {
8378         Tile[newx][newy] = EL_EMPTY;
8379         TEST_DrawLevelField(newx, newy);
8380         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8381       }
8382     }
8383     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8384              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8385               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8386     {
8387       // wait for shrinking amoeba to completely disappear
8388       return;
8389     }
8390     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8391     {
8392       // object was running against a wall
8393
8394       TurnRound(x, y);
8395
8396       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8397         DrawLevelElementAnimation(x, y, element);
8398
8399       if (DONT_TOUCH(element))
8400         TestIfBadThingTouchesPlayer(x, y);
8401
8402       return;
8403     }
8404
8405     InitMovingField(x, y, MovDir[x][y]);
8406
8407     PlayLevelSoundAction(x, y, ACTION_MOVING);
8408   }
8409
8410   if (MovDir[x][y])
8411     ContinueMoving(x, y);
8412 }
8413
8414 void ContinueMoving(int x, int y)
8415 {
8416   int element = Tile[x][y];
8417   struct ElementInfo *ei = &element_info[element];
8418   int direction = MovDir[x][y];
8419   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8420   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8421   int newx = x + dx, newy = y + dy;
8422   int stored = Store[x][y];
8423   int stored_new = Store[newx][newy];
8424   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8425   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8426   boolean last_line = (newy == lev_fieldy - 1);
8427
8428   MovPos[x][y] += getElementMoveStepsize(x, y);
8429
8430   if (pushed_by_player) // special case: moving object pushed by player
8431     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8432
8433   if (ABS(MovPos[x][y]) < TILEX)
8434   {
8435     TEST_DrawLevelField(x, y);
8436
8437     return;     // element is still moving
8438   }
8439
8440   // element reached destination field
8441
8442   Tile[x][y] = EL_EMPTY;
8443   Tile[newx][newy] = element;
8444   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8445
8446   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8447   {
8448     element = Tile[newx][newy] = EL_ACID;
8449   }
8450   else if (element == EL_MOLE)
8451   {
8452     Tile[x][y] = EL_SAND;
8453
8454     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8455   }
8456   else if (element == EL_QUICKSAND_FILLING)
8457   {
8458     element = Tile[newx][newy] = get_next_element(element);
8459     Store[newx][newy] = Store[x][y];
8460   }
8461   else if (element == EL_QUICKSAND_EMPTYING)
8462   {
8463     Tile[x][y] = get_next_element(element);
8464     element = Tile[newx][newy] = Store[x][y];
8465   }
8466   else if (element == EL_QUICKSAND_FAST_FILLING)
8467   {
8468     element = Tile[newx][newy] = get_next_element(element);
8469     Store[newx][newy] = Store[x][y];
8470   }
8471   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8472   {
8473     Tile[x][y] = get_next_element(element);
8474     element = Tile[newx][newy] = Store[x][y];
8475   }
8476   else if (element == EL_MAGIC_WALL_FILLING)
8477   {
8478     element = Tile[newx][newy] = get_next_element(element);
8479     if (!game.magic_wall_active)
8480       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8481     Store[newx][newy] = Store[x][y];
8482   }
8483   else if (element == EL_MAGIC_WALL_EMPTYING)
8484   {
8485     Tile[x][y] = get_next_element(element);
8486     if (!game.magic_wall_active)
8487       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8488     element = Tile[newx][newy] = Store[x][y];
8489
8490     InitField(newx, newy, FALSE);
8491   }
8492   else if (element == EL_BD_MAGIC_WALL_FILLING)
8493   {
8494     element = Tile[newx][newy] = get_next_element(element);
8495     if (!game.magic_wall_active)
8496       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8497     Store[newx][newy] = Store[x][y];
8498   }
8499   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8500   {
8501     Tile[x][y] = get_next_element(element);
8502     if (!game.magic_wall_active)
8503       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8504     element = Tile[newx][newy] = Store[x][y];
8505
8506     InitField(newx, newy, FALSE);
8507   }
8508   else if (element == EL_DC_MAGIC_WALL_FILLING)
8509   {
8510     element = Tile[newx][newy] = get_next_element(element);
8511     if (!game.magic_wall_active)
8512       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8513     Store[newx][newy] = Store[x][y];
8514   }
8515   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8516   {
8517     Tile[x][y] = get_next_element(element);
8518     if (!game.magic_wall_active)
8519       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8520     element = Tile[newx][newy] = Store[x][y];
8521
8522     InitField(newx, newy, FALSE);
8523   }
8524   else if (element == EL_AMOEBA_DROPPING)
8525   {
8526     Tile[x][y] = get_next_element(element);
8527     element = Tile[newx][newy] = Store[x][y];
8528   }
8529   else if (element == EL_SOKOBAN_OBJECT)
8530   {
8531     if (Back[x][y])
8532       Tile[x][y] = Back[x][y];
8533
8534     if (Back[newx][newy])
8535       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8536
8537     Back[x][y] = Back[newx][newy] = 0;
8538   }
8539
8540   Store[x][y] = EL_EMPTY;
8541   MovPos[x][y] = 0;
8542   MovDir[x][y] = 0;
8543   MovDelay[x][y] = 0;
8544
8545   MovDelay[newx][newy] = 0;
8546
8547   if (CAN_CHANGE_OR_HAS_ACTION(element))
8548   {
8549     // copy element change control values to new field
8550     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8551     ChangePage[newx][newy]  = ChangePage[x][y];
8552     ChangeCount[newx][newy] = ChangeCount[x][y];
8553     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8554   }
8555
8556   CustomValue[newx][newy] = CustomValue[x][y];
8557
8558   ChangeDelay[x][y] = 0;
8559   ChangePage[x][y] = -1;
8560   ChangeCount[x][y] = 0;
8561   ChangeEvent[x][y] = -1;
8562
8563   CustomValue[x][y] = 0;
8564
8565   // copy animation control values to new field
8566   GfxFrame[newx][newy]  = GfxFrame[x][y];
8567   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8568   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8569   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8570
8571   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8572
8573   // some elements can leave other elements behind after moving
8574   if (ei->move_leave_element != EL_EMPTY &&
8575       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8576       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8577   {
8578     int move_leave_element = ei->move_leave_element;
8579
8580     // this makes it possible to leave the removed element again
8581     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8582       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8583
8584     Tile[x][y] = move_leave_element;
8585
8586     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8587       MovDir[x][y] = direction;
8588
8589     InitField(x, y, FALSE);
8590
8591     if (GFX_CRUMBLED(Tile[x][y]))
8592       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8593
8594     if (ELEM_IS_PLAYER(move_leave_element))
8595       RelocatePlayer(x, y, move_leave_element);
8596   }
8597
8598   // do this after checking for left-behind element
8599   ResetGfxAnimation(x, y);      // reset animation values for old field
8600
8601   if (!CAN_MOVE(element) ||
8602       (CAN_FALL(element) && direction == MV_DOWN &&
8603        (element == EL_SPRING ||
8604         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8605         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8606     GfxDir[x][y] = MovDir[newx][newy] = 0;
8607
8608   TEST_DrawLevelField(x, y);
8609   TEST_DrawLevelField(newx, newy);
8610
8611   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8612
8613   // prevent pushed element from moving on in pushed direction
8614   if (pushed_by_player && CAN_MOVE(element) &&
8615       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8616       !(element_info[element].move_pattern & direction))
8617     TurnRound(newx, newy);
8618
8619   // prevent elements on conveyor belt from moving on in last direction
8620   if (pushed_by_conveyor && CAN_FALL(element) &&
8621       direction & MV_HORIZONTAL)
8622     MovDir[newx][newy] = 0;
8623
8624   if (!pushed_by_player)
8625   {
8626     int nextx = newx + dx, nexty = newy + dy;
8627     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8628
8629     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8630
8631     if (CAN_FALL(element) && direction == MV_DOWN)
8632       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8633
8634     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8635       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8636
8637     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8638       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8639   }
8640
8641   if (DONT_TOUCH(element))      // object may be nasty to player or others
8642   {
8643     TestIfBadThingTouchesPlayer(newx, newy);
8644     TestIfBadThingTouchesFriend(newx, newy);
8645
8646     if (!IS_CUSTOM_ELEMENT(element))
8647       TestIfBadThingTouchesOtherBadThing(newx, newy);
8648   }
8649   else if (element == EL_PENGUIN)
8650     TestIfFriendTouchesBadThing(newx, newy);
8651
8652   if (DONT_GET_HIT_BY(element))
8653   {
8654     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8655   }
8656
8657   // give the player one last chance (one more frame) to move away
8658   if (CAN_FALL(element) && direction == MV_DOWN &&
8659       (last_line || (!IS_FREE(x, newy + 1) &&
8660                      (!IS_PLAYER(x, newy + 1) ||
8661                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8662     Impact(x, newy);
8663
8664   if (pushed_by_player && !game.use_change_when_pushing_bug)
8665   {
8666     int push_side = MV_DIR_OPPOSITE(direction);
8667     struct PlayerInfo *player = PLAYERINFO(x, y);
8668
8669     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8670                                player->index_bit, push_side);
8671     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8672                                         player->index_bit, push_side);
8673   }
8674
8675   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8676     MovDelay[newx][newy] = 1;
8677
8678   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8679
8680   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8681   TestIfElementHitsCustomElement(newx, newy, direction);
8682   TestIfPlayerTouchesCustomElement(newx, newy);
8683   TestIfElementTouchesCustomElement(newx, newy);
8684
8685   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8686       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8687     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8688                              MV_DIR_OPPOSITE(direction));
8689 }
8690
8691 int AmoebaNeighbourNr(int ax, int ay)
8692 {
8693   int i;
8694   int element = Tile[ax][ay];
8695   int group_nr = 0;
8696   static int xy[4][2] =
8697   {
8698     { 0, -1 },
8699     { -1, 0 },
8700     { +1, 0 },
8701     { 0, +1 }
8702   };
8703
8704   for (i = 0; i < NUM_DIRECTIONS; i++)
8705   {
8706     int x = ax + xy[i][0];
8707     int y = ay + xy[i][1];
8708
8709     if (!IN_LEV_FIELD(x, y))
8710       continue;
8711
8712     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8713       group_nr = AmoebaNr[x][y];
8714   }
8715
8716   return group_nr;
8717 }
8718
8719 static void AmoebaMerge(int ax, int ay)
8720 {
8721   int i, x, y, xx, yy;
8722   int new_group_nr = AmoebaNr[ax][ay];
8723   static int xy[4][2] =
8724   {
8725     { 0, -1 },
8726     { -1, 0 },
8727     { +1, 0 },
8728     { 0, +1 }
8729   };
8730
8731   if (new_group_nr == 0)
8732     return;
8733
8734   for (i = 0; i < NUM_DIRECTIONS; i++)
8735   {
8736     x = ax + xy[i][0];
8737     y = ay + xy[i][1];
8738
8739     if (!IN_LEV_FIELD(x, y))
8740       continue;
8741
8742     if ((Tile[x][y] == EL_AMOEBA_FULL ||
8743          Tile[x][y] == EL_BD_AMOEBA ||
8744          Tile[x][y] == EL_AMOEBA_DEAD) &&
8745         AmoebaNr[x][y] != new_group_nr)
8746     {
8747       int old_group_nr = AmoebaNr[x][y];
8748
8749       if (old_group_nr == 0)
8750         return;
8751
8752       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8753       AmoebaCnt[old_group_nr] = 0;
8754       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8755       AmoebaCnt2[old_group_nr] = 0;
8756
8757       SCAN_PLAYFIELD(xx, yy)
8758       {
8759         if (AmoebaNr[xx][yy] == old_group_nr)
8760           AmoebaNr[xx][yy] = new_group_nr;
8761       }
8762     }
8763   }
8764 }
8765
8766 void AmoebaToDiamond(int ax, int ay)
8767 {
8768   int i, x, y;
8769
8770   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
8771   {
8772     int group_nr = AmoebaNr[ax][ay];
8773
8774 #ifdef DEBUG
8775     if (group_nr == 0)
8776     {
8777       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
8778       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
8779
8780       return;
8781     }
8782 #endif
8783
8784     SCAN_PLAYFIELD(x, y)
8785     {
8786       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8787       {
8788         AmoebaNr[x][y] = 0;
8789         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
8790       }
8791     }
8792
8793     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8794                             SND_AMOEBA_TURNING_TO_GEM :
8795                             SND_AMOEBA_TURNING_TO_ROCK));
8796     Bang(ax, ay);
8797   }
8798   else
8799   {
8800     static int xy[4][2] =
8801     {
8802       { 0, -1 },
8803       { -1, 0 },
8804       { +1, 0 },
8805       { 0, +1 }
8806     };
8807
8808     for (i = 0; i < NUM_DIRECTIONS; i++)
8809     {
8810       x = ax + xy[i][0];
8811       y = ay + xy[i][1];
8812
8813       if (!IN_LEV_FIELD(x, y))
8814         continue;
8815
8816       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
8817       {
8818         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8819                               SND_AMOEBA_TURNING_TO_GEM :
8820                               SND_AMOEBA_TURNING_TO_ROCK));
8821         Bang(x, y);
8822       }
8823     }
8824   }
8825 }
8826
8827 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
8828 {
8829   int x, y;
8830   int group_nr = AmoebaNr[ax][ay];
8831   boolean done = FALSE;
8832
8833 #ifdef DEBUG
8834   if (group_nr == 0)
8835   {
8836     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
8837     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
8838
8839     return;
8840   }
8841 #endif
8842
8843   SCAN_PLAYFIELD(x, y)
8844   {
8845     if (AmoebaNr[x][y] == group_nr &&
8846         (Tile[x][y] == EL_AMOEBA_DEAD ||
8847          Tile[x][y] == EL_BD_AMOEBA ||
8848          Tile[x][y] == EL_AMOEBA_GROWING))
8849     {
8850       AmoebaNr[x][y] = 0;
8851       Tile[x][y] = new_element;
8852       InitField(x, y, FALSE);
8853       TEST_DrawLevelField(x, y);
8854       done = TRUE;
8855     }
8856   }
8857
8858   if (done)
8859     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8860                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8861                             SND_BD_AMOEBA_TURNING_TO_GEM));
8862 }
8863
8864 static void AmoebaGrowing(int x, int y)
8865 {
8866   static unsigned int sound_delay = 0;
8867   static unsigned int sound_delay_value = 0;
8868
8869   if (!MovDelay[x][y])          // start new growing cycle
8870   {
8871     MovDelay[x][y] = 7;
8872
8873     if (DelayReached(&sound_delay, sound_delay_value))
8874     {
8875       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8876       sound_delay_value = 30;
8877     }
8878   }
8879
8880   if (MovDelay[x][y])           // wait some time before growing bigger
8881   {
8882     MovDelay[x][y]--;
8883     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8884     {
8885       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8886                                            6 - MovDelay[x][y]);
8887
8888       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8889     }
8890
8891     if (!MovDelay[x][y])
8892     {
8893       Tile[x][y] = Store[x][y];
8894       Store[x][y] = 0;
8895       TEST_DrawLevelField(x, y);
8896     }
8897   }
8898 }
8899
8900 static void AmoebaShrinking(int x, int y)
8901 {
8902   static unsigned int sound_delay = 0;
8903   static unsigned int sound_delay_value = 0;
8904
8905   if (!MovDelay[x][y])          // start new shrinking cycle
8906   {
8907     MovDelay[x][y] = 7;
8908
8909     if (DelayReached(&sound_delay, sound_delay_value))
8910       sound_delay_value = 30;
8911   }
8912
8913   if (MovDelay[x][y])           // wait some time before shrinking
8914   {
8915     MovDelay[x][y]--;
8916     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8917     {
8918       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8919                                            6 - MovDelay[x][y]);
8920
8921       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8922     }
8923
8924     if (!MovDelay[x][y])
8925     {
8926       Tile[x][y] = EL_EMPTY;
8927       TEST_DrawLevelField(x, y);
8928
8929       // don't let mole enter this field in this cycle;
8930       // (give priority to objects falling to this field from above)
8931       Stop[x][y] = TRUE;
8932     }
8933   }
8934 }
8935
8936 static void AmoebaReproduce(int ax, int ay)
8937 {
8938   int i;
8939   int element = Tile[ax][ay];
8940   int graphic = el2img(element);
8941   int newax = ax, neway = ay;
8942   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8943   static int xy[4][2] =
8944   {
8945     { 0, -1 },
8946     { -1, 0 },
8947     { +1, 0 },
8948     { 0, +1 }
8949   };
8950
8951   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8952   {
8953     Tile[ax][ay] = EL_AMOEBA_DEAD;
8954     TEST_DrawLevelField(ax, ay);
8955     return;
8956   }
8957
8958   if (IS_ANIMATED(graphic))
8959     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8960
8961   if (!MovDelay[ax][ay])        // start making new amoeba field
8962     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8963
8964   if (MovDelay[ax][ay])         // wait some time before making new amoeba
8965   {
8966     MovDelay[ax][ay]--;
8967     if (MovDelay[ax][ay])
8968       return;
8969   }
8970
8971   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
8972   {
8973     int start = RND(4);
8974     int x = ax + xy[start][0];
8975     int y = ay + xy[start][1];
8976
8977     if (!IN_LEV_FIELD(x, y))
8978       return;
8979
8980     if (IS_FREE(x, y) ||
8981         CAN_GROW_INTO(Tile[x][y]) ||
8982         Tile[x][y] == EL_QUICKSAND_EMPTY ||
8983         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
8984     {
8985       newax = x;
8986       neway = y;
8987     }
8988
8989     if (newax == ax && neway == ay)
8990       return;
8991   }
8992   else                          // normal or "filled" (BD style) amoeba
8993   {
8994     int start = RND(4);
8995     boolean waiting_for_player = FALSE;
8996
8997     for (i = 0; i < NUM_DIRECTIONS; i++)
8998     {
8999       int j = (start + i) % 4;
9000       int x = ax + xy[j][0];
9001       int y = ay + xy[j][1];
9002
9003       if (!IN_LEV_FIELD(x, y))
9004         continue;
9005
9006       if (IS_FREE(x, y) ||
9007           CAN_GROW_INTO(Tile[x][y]) ||
9008           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9009           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9010       {
9011         newax = x;
9012         neway = y;
9013         break;
9014       }
9015       else if (IS_PLAYER(x, y))
9016         waiting_for_player = TRUE;
9017     }
9018
9019     if (newax == ax && neway == ay)             // amoeba cannot grow
9020     {
9021       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9022       {
9023         Tile[ax][ay] = EL_AMOEBA_DEAD;
9024         TEST_DrawLevelField(ax, ay);
9025         AmoebaCnt[AmoebaNr[ax][ay]]--;
9026
9027         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9028         {
9029           if (element == EL_AMOEBA_FULL)
9030             AmoebaToDiamond(ax, ay);
9031           else if (element == EL_BD_AMOEBA)
9032             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9033         }
9034       }
9035       return;
9036     }
9037     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9038     {
9039       // amoeba gets larger by growing in some direction
9040
9041       int new_group_nr = AmoebaNr[ax][ay];
9042
9043 #ifdef DEBUG
9044   if (new_group_nr == 0)
9045   {
9046     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9047           newax, neway);
9048     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9049
9050     return;
9051   }
9052 #endif
9053
9054       AmoebaNr[newax][neway] = new_group_nr;
9055       AmoebaCnt[new_group_nr]++;
9056       AmoebaCnt2[new_group_nr]++;
9057
9058       // if amoeba touches other amoeba(s) after growing, unify them
9059       AmoebaMerge(newax, neway);
9060
9061       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9062       {
9063         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9064         return;
9065       }
9066     }
9067   }
9068
9069   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9070       (neway == lev_fieldy - 1 && newax != ax))
9071   {
9072     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9073     Store[newax][neway] = element;
9074   }
9075   else if (neway == ay || element == EL_EMC_DRIPPER)
9076   {
9077     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9078
9079     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9080   }
9081   else
9082   {
9083     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9084     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9085     Store[ax][ay] = EL_AMOEBA_DROP;
9086     ContinueMoving(ax, ay);
9087     return;
9088   }
9089
9090   TEST_DrawLevelField(newax, neway);
9091 }
9092
9093 static void Life(int ax, int ay)
9094 {
9095   int x1, y1, x2, y2;
9096   int life_time = 40;
9097   int element = Tile[ax][ay];
9098   int graphic = el2img(element);
9099   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9100                          level.biomaze);
9101   boolean changed = FALSE;
9102
9103   if (IS_ANIMATED(graphic))
9104     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9105
9106   if (Stop[ax][ay])
9107     return;
9108
9109   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9110     MovDelay[ax][ay] = life_time;
9111
9112   if (MovDelay[ax][ay])         // wait some time before next cycle
9113   {
9114     MovDelay[ax][ay]--;
9115     if (MovDelay[ax][ay])
9116       return;
9117   }
9118
9119   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9120   {
9121     int xx = ax+x1, yy = ay+y1;
9122     int old_element = Tile[xx][yy];
9123     int num_neighbours = 0;
9124
9125     if (!IN_LEV_FIELD(xx, yy))
9126       continue;
9127
9128     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9129     {
9130       int x = xx+x2, y = yy+y2;
9131
9132       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9133         continue;
9134
9135       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9136       boolean is_neighbour = FALSE;
9137
9138       if (level.use_life_bugs)
9139         is_neighbour =
9140           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9141            (IS_FREE(x, y)                             &&  Stop[x][y]));
9142       else
9143         is_neighbour =
9144           (Last[x][y] == element || is_player_cell);
9145
9146       if (is_neighbour)
9147         num_neighbours++;
9148     }
9149
9150     boolean is_free = FALSE;
9151
9152     if (level.use_life_bugs)
9153       is_free = (IS_FREE(xx, yy));
9154     else
9155       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9156
9157     if (xx == ax && yy == ay)           // field in the middle
9158     {
9159       if (num_neighbours < life_parameter[0] ||
9160           num_neighbours > life_parameter[1])
9161       {
9162         Tile[xx][yy] = EL_EMPTY;
9163         if (Tile[xx][yy] != old_element)
9164           TEST_DrawLevelField(xx, yy);
9165         Stop[xx][yy] = TRUE;
9166         changed = TRUE;
9167       }
9168     }
9169     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9170     {                                   // free border field
9171       if (num_neighbours >= life_parameter[2] &&
9172           num_neighbours <= life_parameter[3])
9173       {
9174         Tile[xx][yy] = element;
9175         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9176         if (Tile[xx][yy] != old_element)
9177           TEST_DrawLevelField(xx, yy);
9178         Stop[xx][yy] = TRUE;
9179         changed = TRUE;
9180       }
9181     }
9182   }
9183
9184   if (changed)
9185     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9186                    SND_GAME_OF_LIFE_GROWING);
9187 }
9188
9189 static void InitRobotWheel(int x, int y)
9190 {
9191   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9192 }
9193
9194 static void RunRobotWheel(int x, int y)
9195 {
9196   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9197 }
9198
9199 static void StopRobotWheel(int x, int y)
9200 {
9201   if (game.robot_wheel_x == x &&
9202       game.robot_wheel_y == y)
9203   {
9204     game.robot_wheel_x = -1;
9205     game.robot_wheel_y = -1;
9206     game.robot_wheel_active = FALSE;
9207   }
9208 }
9209
9210 static void InitTimegateWheel(int x, int y)
9211 {
9212   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9213 }
9214
9215 static void RunTimegateWheel(int x, int y)
9216 {
9217   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9218 }
9219
9220 static void InitMagicBallDelay(int x, int y)
9221 {
9222   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9223 }
9224
9225 static void ActivateMagicBall(int bx, int by)
9226 {
9227   int x, y;
9228
9229   if (level.ball_random)
9230   {
9231     int pos_border = RND(8);    // select one of the eight border elements
9232     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9233     int xx = pos_content % 3;
9234     int yy = pos_content / 3;
9235
9236     x = bx - 1 + xx;
9237     y = by - 1 + yy;
9238
9239     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9240       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9241   }
9242   else
9243   {
9244     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9245     {
9246       int xx = x - bx + 1;
9247       int yy = y - by + 1;
9248
9249       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9250         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9251     }
9252   }
9253
9254   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9255 }
9256
9257 static void CheckExit(int x, int y)
9258 {
9259   if (game.gems_still_needed > 0 ||
9260       game.sokoban_fields_still_needed > 0 ||
9261       game.sokoban_objects_still_needed > 0 ||
9262       game.lights_still_needed > 0)
9263   {
9264     int element = Tile[x][y];
9265     int graphic = el2img(element);
9266
9267     if (IS_ANIMATED(graphic))
9268       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9269
9270     return;
9271   }
9272
9273   // do not re-open exit door closed after last player
9274   if (game.all_players_gone)
9275     return;
9276
9277   Tile[x][y] = EL_EXIT_OPENING;
9278
9279   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9280 }
9281
9282 static void CheckExitEM(int x, int y)
9283 {
9284   if (game.gems_still_needed > 0 ||
9285       game.sokoban_fields_still_needed > 0 ||
9286       game.sokoban_objects_still_needed > 0 ||
9287       game.lights_still_needed > 0)
9288   {
9289     int element = Tile[x][y];
9290     int graphic = el2img(element);
9291
9292     if (IS_ANIMATED(graphic))
9293       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9294
9295     return;
9296   }
9297
9298   // do not re-open exit door closed after last player
9299   if (game.all_players_gone)
9300     return;
9301
9302   Tile[x][y] = EL_EM_EXIT_OPENING;
9303
9304   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9305 }
9306
9307 static void CheckExitSteel(int x, int y)
9308 {
9309   if (game.gems_still_needed > 0 ||
9310       game.sokoban_fields_still_needed > 0 ||
9311       game.sokoban_objects_still_needed > 0 ||
9312       game.lights_still_needed > 0)
9313   {
9314     int element = Tile[x][y];
9315     int graphic = el2img(element);
9316
9317     if (IS_ANIMATED(graphic))
9318       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9319
9320     return;
9321   }
9322
9323   // do not re-open exit door closed after last player
9324   if (game.all_players_gone)
9325     return;
9326
9327   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9328
9329   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9330 }
9331
9332 static void CheckExitSteelEM(int x, int y)
9333 {
9334   if (game.gems_still_needed > 0 ||
9335       game.sokoban_fields_still_needed > 0 ||
9336       game.sokoban_objects_still_needed > 0 ||
9337       game.lights_still_needed > 0)
9338   {
9339     int element = Tile[x][y];
9340     int graphic = el2img(element);
9341
9342     if (IS_ANIMATED(graphic))
9343       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9344
9345     return;
9346   }
9347
9348   // do not re-open exit door closed after last player
9349   if (game.all_players_gone)
9350     return;
9351
9352   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9353
9354   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9355 }
9356
9357 static void CheckExitSP(int x, int y)
9358 {
9359   if (game.gems_still_needed > 0)
9360   {
9361     int element = Tile[x][y];
9362     int graphic = el2img(element);
9363
9364     if (IS_ANIMATED(graphic))
9365       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9366
9367     return;
9368   }
9369
9370   // do not re-open exit door closed after last player
9371   if (game.all_players_gone)
9372     return;
9373
9374   Tile[x][y] = EL_SP_EXIT_OPENING;
9375
9376   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9377 }
9378
9379 static void CloseAllOpenTimegates(void)
9380 {
9381   int x, y;
9382
9383   SCAN_PLAYFIELD(x, y)
9384   {
9385     int element = Tile[x][y];
9386
9387     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9388     {
9389       Tile[x][y] = EL_TIMEGATE_CLOSING;
9390
9391       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9392     }
9393   }
9394 }
9395
9396 static void DrawTwinkleOnField(int x, int y)
9397 {
9398   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9399     return;
9400
9401   if (Tile[x][y] == EL_BD_DIAMOND)
9402     return;
9403
9404   if (MovDelay[x][y] == 0)      // next animation frame
9405     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9406
9407   if (MovDelay[x][y] != 0)      // wait some time before next frame
9408   {
9409     MovDelay[x][y]--;
9410
9411     DrawLevelElementAnimation(x, y, Tile[x][y]);
9412
9413     if (MovDelay[x][y] != 0)
9414     {
9415       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9416                                            10 - MovDelay[x][y]);
9417
9418       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9419     }
9420   }
9421 }
9422
9423 static void MauerWaechst(int x, int y)
9424 {
9425   int delay = 6;
9426
9427   if (!MovDelay[x][y])          // next animation frame
9428     MovDelay[x][y] = 3 * delay;
9429
9430   if (MovDelay[x][y])           // wait some time before next frame
9431   {
9432     MovDelay[x][y]--;
9433
9434     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9435     {
9436       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9437       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9438
9439       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9440     }
9441
9442     if (!MovDelay[x][y])
9443     {
9444       if (MovDir[x][y] == MV_LEFT)
9445       {
9446         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9447           TEST_DrawLevelField(x - 1, y);
9448       }
9449       else if (MovDir[x][y] == MV_RIGHT)
9450       {
9451         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9452           TEST_DrawLevelField(x + 1, y);
9453       }
9454       else if (MovDir[x][y] == MV_UP)
9455       {
9456         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9457           TEST_DrawLevelField(x, y - 1);
9458       }
9459       else
9460       {
9461         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9462           TEST_DrawLevelField(x, y + 1);
9463       }
9464
9465       Tile[x][y] = Store[x][y];
9466       Store[x][y] = 0;
9467       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9468       TEST_DrawLevelField(x, y);
9469     }
9470   }
9471 }
9472
9473 static void MauerAbleger(int ax, int ay)
9474 {
9475   int element = Tile[ax][ay];
9476   int graphic = el2img(element);
9477   boolean oben_frei = FALSE, unten_frei = FALSE;
9478   boolean links_frei = FALSE, rechts_frei = FALSE;
9479   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9480   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9481   boolean new_wall = FALSE;
9482
9483   if (IS_ANIMATED(graphic))
9484     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9485
9486   if (!MovDelay[ax][ay])        // start building new wall
9487     MovDelay[ax][ay] = 6;
9488
9489   if (MovDelay[ax][ay])         // wait some time before building new wall
9490   {
9491     MovDelay[ax][ay]--;
9492     if (MovDelay[ax][ay])
9493       return;
9494   }
9495
9496   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9497     oben_frei = TRUE;
9498   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9499     unten_frei = TRUE;
9500   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9501     links_frei = TRUE;
9502   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9503     rechts_frei = TRUE;
9504
9505   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9506       element == EL_EXPANDABLE_WALL_ANY)
9507   {
9508     if (oben_frei)
9509     {
9510       Tile[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9511       Store[ax][ay-1] = element;
9512       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9513       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9514         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9515                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9516       new_wall = TRUE;
9517     }
9518     if (unten_frei)
9519     {
9520       Tile[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9521       Store[ax][ay+1] = element;
9522       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9523       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9524         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9525                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9526       new_wall = TRUE;
9527     }
9528   }
9529
9530   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9531       element == EL_EXPANDABLE_WALL_ANY ||
9532       element == EL_EXPANDABLE_WALL ||
9533       element == EL_BD_EXPANDABLE_WALL)
9534   {
9535     if (links_frei)
9536     {
9537       Tile[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9538       Store[ax-1][ay] = element;
9539       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9540       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9541         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9542                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9543       new_wall = TRUE;
9544     }
9545
9546     if (rechts_frei)
9547     {
9548       Tile[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9549       Store[ax+1][ay] = element;
9550       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9551       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9552         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9553                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9554       new_wall = TRUE;
9555     }
9556   }
9557
9558   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9559     TEST_DrawLevelField(ax, ay);
9560
9561   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9562     oben_massiv = TRUE;
9563   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9564     unten_massiv = TRUE;
9565   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9566     links_massiv = TRUE;
9567   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9568     rechts_massiv = TRUE;
9569
9570   if (((oben_massiv && unten_massiv) ||
9571        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9572        element == EL_EXPANDABLE_WALL) &&
9573       ((links_massiv && rechts_massiv) ||
9574        element == EL_EXPANDABLE_WALL_VERTICAL))
9575     Tile[ax][ay] = EL_WALL;
9576
9577   if (new_wall)
9578     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9579 }
9580
9581 static void MauerAblegerStahl(int ax, int ay)
9582 {
9583   int element = Tile[ax][ay];
9584   int graphic = el2img(element);
9585   boolean oben_frei = FALSE, unten_frei = FALSE;
9586   boolean links_frei = FALSE, rechts_frei = FALSE;
9587   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9588   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9589   boolean new_wall = FALSE;
9590
9591   if (IS_ANIMATED(graphic))
9592     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9593
9594   if (!MovDelay[ax][ay])        // start building new wall
9595     MovDelay[ax][ay] = 6;
9596
9597   if (MovDelay[ax][ay])         // wait some time before building new wall
9598   {
9599     MovDelay[ax][ay]--;
9600     if (MovDelay[ax][ay])
9601       return;
9602   }
9603
9604   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9605     oben_frei = TRUE;
9606   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9607     unten_frei = TRUE;
9608   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9609     links_frei = TRUE;
9610   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9611     rechts_frei = TRUE;
9612
9613   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9614       element == EL_EXPANDABLE_STEELWALL_ANY)
9615   {
9616     if (oben_frei)
9617     {
9618       Tile[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9619       Store[ax][ay-1] = element;
9620       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9621       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9622         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9623                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9624       new_wall = TRUE;
9625     }
9626     if (unten_frei)
9627     {
9628       Tile[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9629       Store[ax][ay+1] = element;
9630       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9631       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9632         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9633                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9634       new_wall = TRUE;
9635     }
9636   }
9637
9638   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9639       element == EL_EXPANDABLE_STEELWALL_ANY)
9640   {
9641     if (links_frei)
9642     {
9643       Tile[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9644       Store[ax-1][ay] = element;
9645       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9646       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9647         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9648                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9649       new_wall = TRUE;
9650     }
9651
9652     if (rechts_frei)
9653     {
9654       Tile[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9655       Store[ax+1][ay] = element;
9656       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9657       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9658         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9659                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9660       new_wall = TRUE;
9661     }
9662   }
9663
9664   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9665     oben_massiv = TRUE;
9666   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9667     unten_massiv = TRUE;
9668   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9669     links_massiv = TRUE;
9670   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9671     rechts_massiv = TRUE;
9672
9673   if (((oben_massiv && unten_massiv) ||
9674        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9675       ((links_massiv && rechts_massiv) ||
9676        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9677     Tile[ax][ay] = EL_STEELWALL;
9678
9679   if (new_wall)
9680     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9681 }
9682
9683 static void CheckForDragon(int x, int y)
9684 {
9685   int i, j;
9686   boolean dragon_found = FALSE;
9687   static int xy[4][2] =
9688   {
9689     { 0, -1 },
9690     { -1, 0 },
9691     { +1, 0 },
9692     { 0, +1 }
9693   };
9694
9695   for (i = 0; i < NUM_DIRECTIONS; i++)
9696   {
9697     for (j = 0; j < 4; j++)
9698     {
9699       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9700
9701       if (IN_LEV_FIELD(xx, yy) &&
9702           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9703       {
9704         if (Tile[xx][yy] == EL_DRAGON)
9705           dragon_found = TRUE;
9706       }
9707       else
9708         break;
9709     }
9710   }
9711
9712   if (!dragon_found)
9713   {
9714     for (i = 0; i < NUM_DIRECTIONS; i++)
9715     {
9716       for (j = 0; j < 3; j++)
9717       {
9718         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9719   
9720         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9721         {
9722           Tile[xx][yy] = EL_EMPTY;
9723           TEST_DrawLevelField(xx, yy);
9724         }
9725         else
9726           break;
9727       }
9728     }
9729   }
9730 }
9731
9732 static void InitBuggyBase(int x, int y)
9733 {
9734   int element = Tile[x][y];
9735   int activating_delay = FRAMES_PER_SECOND / 4;
9736
9737   ChangeDelay[x][y] =
9738     (element == EL_SP_BUGGY_BASE ?
9739      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9740      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9741      activating_delay :
9742      element == EL_SP_BUGGY_BASE_ACTIVE ?
9743      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9744 }
9745
9746 static void WarnBuggyBase(int x, int y)
9747 {
9748   int i;
9749   static int xy[4][2] =
9750   {
9751     { 0, -1 },
9752     { -1, 0 },
9753     { +1, 0 },
9754     { 0, +1 }
9755   };
9756
9757   for (i = 0; i < NUM_DIRECTIONS; i++)
9758   {
9759     int xx = x + xy[i][0];
9760     int yy = y + xy[i][1];
9761
9762     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9763     {
9764       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9765
9766       break;
9767     }
9768   }
9769 }
9770
9771 static void InitTrap(int x, int y)
9772 {
9773   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9774 }
9775
9776 static void ActivateTrap(int x, int y)
9777 {
9778   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9779 }
9780
9781 static void ChangeActiveTrap(int x, int y)
9782 {
9783   int graphic = IMG_TRAP_ACTIVE;
9784
9785   // if new animation frame was drawn, correct crumbled sand border
9786   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9787     TEST_DrawLevelFieldCrumbled(x, y);
9788 }
9789
9790 static int getSpecialActionElement(int element, int number, int base_element)
9791 {
9792   return (element != EL_EMPTY ? element :
9793           number != -1 ? base_element + number - 1 :
9794           EL_EMPTY);
9795 }
9796
9797 static int getModifiedActionNumber(int value_old, int operator, int operand,
9798                                    int value_min, int value_max)
9799 {
9800   int value_new = (operator == CA_MODE_SET      ? operand :
9801                    operator == CA_MODE_ADD      ? value_old + operand :
9802                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9803                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9804                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9805                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9806                    value_old);
9807
9808   return (value_new < value_min ? value_min :
9809           value_new > value_max ? value_max :
9810           value_new);
9811 }
9812
9813 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9814 {
9815   struct ElementInfo *ei = &element_info[element];
9816   struct ElementChangeInfo *change = &ei->change_page[page];
9817   int target_element = change->target_element;
9818   int action_type = change->action_type;
9819   int action_mode = change->action_mode;
9820   int action_arg = change->action_arg;
9821   int action_element = change->action_element;
9822   int i;
9823
9824   if (!change->has_action)
9825     return;
9826
9827   // ---------- determine action paramater values -----------------------------
9828
9829   int level_time_value =
9830     (level.time > 0 ? TimeLeft :
9831      TimePlayed);
9832
9833   int action_arg_element_raw =
9834     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9835      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9836      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9837      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9838      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9839      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9840      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9841      EL_EMPTY);
9842   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9843
9844   int action_arg_direction =
9845     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9846      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9847      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9848      change->actual_trigger_side :
9849      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9850      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9851      MV_NONE);
9852
9853   int action_arg_number_min =
9854     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9855      CA_ARG_MIN);
9856
9857   int action_arg_number_max =
9858     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9859      action_type == CA_SET_LEVEL_GEMS ? 999 :
9860      action_type == CA_SET_LEVEL_TIME ? 9999 :
9861      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9862      action_type == CA_SET_CE_VALUE ? 9999 :
9863      action_type == CA_SET_CE_SCORE ? 9999 :
9864      CA_ARG_MAX);
9865
9866   int action_arg_number_reset =
9867     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9868      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9869      action_type == CA_SET_LEVEL_TIME ? level.time :
9870      action_type == CA_SET_LEVEL_SCORE ? 0 :
9871      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9872      action_type == CA_SET_CE_SCORE ? 0 :
9873      0);
9874
9875   int action_arg_number =
9876     (action_arg <= CA_ARG_MAX ? action_arg :
9877      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9878      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9879      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9880      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9881      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9882      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9883      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9884      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9885      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9886      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9887      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
9888      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
9889      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9890      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9891      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9892      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9893      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9894      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9895      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9896      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9897      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9898      -1);
9899
9900   int action_arg_number_old =
9901     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
9902      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9903      action_type == CA_SET_LEVEL_SCORE ? game.score :
9904      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9905      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9906      0);
9907
9908   int action_arg_number_new =
9909     getModifiedActionNumber(action_arg_number_old,
9910                             action_mode, action_arg_number,
9911                             action_arg_number_min, action_arg_number_max);
9912
9913   int trigger_player_bits =
9914     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9915      change->actual_trigger_player_bits : change->trigger_player);
9916
9917   int action_arg_player_bits =
9918     (action_arg >= CA_ARG_PLAYER_1 &&
9919      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9920      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9921      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9922      PLAYER_BITS_ANY);
9923
9924   // ---------- execute action  -----------------------------------------------
9925
9926   switch (action_type)
9927   {
9928     case CA_NO_ACTION:
9929     {
9930       return;
9931     }
9932
9933     // ---------- level actions  ----------------------------------------------
9934
9935     case CA_RESTART_LEVEL:
9936     {
9937       game.restart_level = TRUE;
9938
9939       break;
9940     }
9941
9942     case CA_SHOW_ENVELOPE:
9943     {
9944       int element = getSpecialActionElement(action_arg_element,
9945                                             action_arg_number, EL_ENVELOPE_1);
9946
9947       if (IS_ENVELOPE(element))
9948         local_player->show_envelope = element;
9949
9950       break;
9951     }
9952
9953     case CA_SET_LEVEL_TIME:
9954     {
9955       if (level.time > 0)       // only modify limited time value
9956       {
9957         TimeLeft = action_arg_number_new;
9958
9959         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9960
9961         DisplayGameControlValues();
9962
9963         if (!TimeLeft && setup.time_limit)
9964           for (i = 0; i < MAX_PLAYERS; i++)
9965             KillPlayer(&stored_player[i]);
9966       }
9967
9968       break;
9969     }
9970
9971     case CA_SET_LEVEL_SCORE:
9972     {
9973       game.score = action_arg_number_new;
9974
9975       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
9976
9977       DisplayGameControlValues();
9978
9979       break;
9980     }
9981
9982     case CA_SET_LEVEL_GEMS:
9983     {
9984       game.gems_still_needed = action_arg_number_new;
9985
9986       game.snapshot.collected_item = TRUE;
9987
9988       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
9989
9990       DisplayGameControlValues();
9991
9992       break;
9993     }
9994
9995     case CA_SET_LEVEL_WIND:
9996     {
9997       game.wind_direction = action_arg_direction;
9998
9999       break;
10000     }
10001
10002     case CA_SET_LEVEL_RANDOM_SEED:
10003     {
10004       // ensure that setting a new random seed while playing is predictable
10005       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10006
10007       break;
10008     }
10009
10010     // ---------- player actions  ---------------------------------------------
10011
10012     case CA_MOVE_PLAYER:
10013     case CA_MOVE_PLAYER_NEW:
10014     {
10015       // automatically move to the next field in specified direction
10016       for (i = 0; i < MAX_PLAYERS; i++)
10017         if (trigger_player_bits & (1 << i))
10018           if (action_type == CA_MOVE_PLAYER ||
10019               stored_player[i].MovPos == 0)
10020             stored_player[i].programmed_action = action_arg_direction;
10021
10022       break;
10023     }
10024
10025     case CA_EXIT_PLAYER:
10026     {
10027       for (i = 0; i < MAX_PLAYERS; i++)
10028         if (action_arg_player_bits & (1 << i))
10029           ExitPlayer(&stored_player[i]);
10030
10031       if (game.players_still_needed == 0)
10032         LevelSolved();
10033
10034       break;
10035     }
10036
10037     case CA_KILL_PLAYER:
10038     {
10039       for (i = 0; i < MAX_PLAYERS; i++)
10040         if (action_arg_player_bits & (1 << i))
10041           KillPlayer(&stored_player[i]);
10042
10043       break;
10044     }
10045
10046     case CA_SET_PLAYER_KEYS:
10047     {
10048       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10049       int element = getSpecialActionElement(action_arg_element,
10050                                             action_arg_number, EL_KEY_1);
10051
10052       if (IS_KEY(element))
10053       {
10054         for (i = 0; i < MAX_PLAYERS; i++)
10055         {
10056           if (trigger_player_bits & (1 << i))
10057           {
10058             stored_player[i].key[KEY_NR(element)] = key_state;
10059
10060             DrawGameDoorValues();
10061           }
10062         }
10063       }
10064
10065       break;
10066     }
10067
10068     case CA_SET_PLAYER_SPEED:
10069     {
10070       for (i = 0; i < MAX_PLAYERS; i++)
10071       {
10072         if (trigger_player_bits & (1 << i))
10073         {
10074           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10075
10076           if (action_arg == CA_ARG_SPEED_FASTER &&
10077               stored_player[i].cannot_move)
10078           {
10079             action_arg_number = STEPSIZE_VERY_SLOW;
10080           }
10081           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10082                    action_arg == CA_ARG_SPEED_FASTER)
10083           {
10084             action_arg_number = 2;
10085             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10086                            CA_MODE_MULTIPLY);
10087           }
10088           else if (action_arg == CA_ARG_NUMBER_RESET)
10089           {
10090             action_arg_number = level.initial_player_stepsize[i];
10091           }
10092
10093           move_stepsize =
10094             getModifiedActionNumber(move_stepsize,
10095                                     action_mode,
10096                                     action_arg_number,
10097                                     action_arg_number_min,
10098                                     action_arg_number_max);
10099
10100           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10101         }
10102       }
10103
10104       break;
10105     }
10106
10107     case CA_SET_PLAYER_SHIELD:
10108     {
10109       for (i = 0; i < MAX_PLAYERS; i++)
10110       {
10111         if (trigger_player_bits & (1 << i))
10112         {
10113           if (action_arg == CA_ARG_SHIELD_OFF)
10114           {
10115             stored_player[i].shield_normal_time_left = 0;
10116             stored_player[i].shield_deadly_time_left = 0;
10117           }
10118           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10119           {
10120             stored_player[i].shield_normal_time_left = 999999;
10121           }
10122           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10123           {
10124             stored_player[i].shield_normal_time_left = 999999;
10125             stored_player[i].shield_deadly_time_left = 999999;
10126           }
10127         }
10128       }
10129
10130       break;
10131     }
10132
10133     case CA_SET_PLAYER_GRAVITY:
10134     {
10135       for (i = 0; i < MAX_PLAYERS; i++)
10136       {
10137         if (trigger_player_bits & (1 << i))
10138         {
10139           stored_player[i].gravity =
10140             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10141              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10142              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10143              stored_player[i].gravity);
10144         }
10145       }
10146
10147       break;
10148     }
10149
10150     case CA_SET_PLAYER_ARTWORK:
10151     {
10152       for (i = 0; i < MAX_PLAYERS; i++)
10153       {
10154         if (trigger_player_bits & (1 << i))
10155         {
10156           int artwork_element = action_arg_element;
10157
10158           if (action_arg == CA_ARG_ELEMENT_RESET)
10159             artwork_element =
10160               (level.use_artwork_element[i] ? level.artwork_element[i] :
10161                stored_player[i].element_nr);
10162
10163           if (stored_player[i].artwork_element != artwork_element)
10164             stored_player[i].Frame = 0;
10165
10166           stored_player[i].artwork_element = artwork_element;
10167
10168           SetPlayerWaiting(&stored_player[i], FALSE);
10169
10170           // set number of special actions for bored and sleeping animation
10171           stored_player[i].num_special_action_bored =
10172             get_num_special_action(artwork_element,
10173                                    ACTION_BORING_1, ACTION_BORING_LAST);
10174           stored_player[i].num_special_action_sleeping =
10175             get_num_special_action(artwork_element,
10176                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10177         }
10178       }
10179
10180       break;
10181     }
10182
10183     case CA_SET_PLAYER_INVENTORY:
10184     {
10185       for (i = 0; i < MAX_PLAYERS; i++)
10186       {
10187         struct PlayerInfo *player = &stored_player[i];
10188         int j, k;
10189
10190         if (trigger_player_bits & (1 << i))
10191         {
10192           int inventory_element = action_arg_element;
10193
10194           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10195               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10196               action_arg == CA_ARG_ELEMENT_ACTION)
10197           {
10198             int element = inventory_element;
10199             int collect_count = element_info[element].collect_count_initial;
10200
10201             if (!IS_CUSTOM_ELEMENT(element))
10202               collect_count = 1;
10203
10204             if (collect_count == 0)
10205               player->inventory_infinite_element = element;
10206             else
10207               for (k = 0; k < collect_count; k++)
10208                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10209                   player->inventory_element[player->inventory_size++] =
10210                     element;
10211           }
10212           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10213                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10214                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10215           {
10216             if (player->inventory_infinite_element != EL_UNDEFINED &&
10217                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10218                                      action_arg_element_raw))
10219               player->inventory_infinite_element = EL_UNDEFINED;
10220
10221             for (k = 0, j = 0; j < player->inventory_size; j++)
10222             {
10223               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10224                                         action_arg_element_raw))
10225                 player->inventory_element[k++] = player->inventory_element[j];
10226             }
10227
10228             player->inventory_size = k;
10229           }
10230           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10231           {
10232             if (player->inventory_size > 0)
10233             {
10234               for (j = 0; j < player->inventory_size - 1; j++)
10235                 player->inventory_element[j] = player->inventory_element[j + 1];
10236
10237               player->inventory_size--;
10238             }
10239           }
10240           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10241           {
10242             if (player->inventory_size > 0)
10243               player->inventory_size--;
10244           }
10245           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10246           {
10247             player->inventory_infinite_element = EL_UNDEFINED;
10248             player->inventory_size = 0;
10249           }
10250           else if (action_arg == CA_ARG_INVENTORY_RESET)
10251           {
10252             player->inventory_infinite_element = EL_UNDEFINED;
10253             player->inventory_size = 0;
10254
10255             if (level.use_initial_inventory[i])
10256             {
10257               for (j = 0; j < level.initial_inventory_size[i]; j++)
10258               {
10259                 int element = level.initial_inventory_content[i][j];
10260                 int collect_count = element_info[element].collect_count_initial;
10261
10262                 if (!IS_CUSTOM_ELEMENT(element))
10263                   collect_count = 1;
10264
10265                 if (collect_count == 0)
10266                   player->inventory_infinite_element = element;
10267                 else
10268                   for (k = 0; k < collect_count; k++)
10269                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10270                       player->inventory_element[player->inventory_size++] =
10271                         element;
10272               }
10273             }
10274           }
10275         }
10276       }
10277
10278       break;
10279     }
10280
10281     // ---------- CE actions  -------------------------------------------------
10282
10283     case CA_SET_CE_VALUE:
10284     {
10285       int last_ce_value = CustomValue[x][y];
10286
10287       CustomValue[x][y] = action_arg_number_new;
10288
10289       if (CustomValue[x][y] != last_ce_value)
10290       {
10291         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10292         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10293
10294         if (CustomValue[x][y] == 0)
10295         {
10296           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10297           ChangeCount[x][y] = 0;        // allow at least one more change
10298
10299           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10300           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10301         }
10302       }
10303
10304       break;
10305     }
10306
10307     case CA_SET_CE_SCORE:
10308     {
10309       int last_ce_score = ei->collect_score;
10310
10311       ei->collect_score = action_arg_number_new;
10312
10313       if (ei->collect_score != last_ce_score)
10314       {
10315         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10316         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10317
10318         if (ei->collect_score == 0)
10319         {
10320           int xx, yy;
10321
10322           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10323           ChangeCount[x][y] = 0;        // allow at least one more change
10324
10325           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10326           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10327
10328           /*
10329             This is a very special case that seems to be a mixture between
10330             CheckElementChange() and CheckTriggeredElementChange(): while
10331             the first one only affects single elements that are triggered
10332             directly, the second one affects multiple elements in the playfield
10333             that are triggered indirectly by another element. This is a third
10334             case: Changing the CE score always affects multiple identical CEs,
10335             so every affected CE must be checked, not only the single CE for
10336             which the CE score was changed in the first place (as every instance
10337             of that CE shares the same CE score, and therefore also can change)!
10338           */
10339           SCAN_PLAYFIELD(xx, yy)
10340           {
10341             if (Tile[xx][yy] == element)
10342               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10343                                  CE_SCORE_GETS_ZERO);
10344           }
10345         }
10346       }
10347
10348       break;
10349     }
10350
10351     case CA_SET_CE_ARTWORK:
10352     {
10353       int artwork_element = action_arg_element;
10354       boolean reset_frame = FALSE;
10355       int xx, yy;
10356
10357       if (action_arg == CA_ARG_ELEMENT_RESET)
10358         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10359                            element);
10360
10361       if (ei->gfx_element != artwork_element)
10362         reset_frame = TRUE;
10363
10364       ei->gfx_element = artwork_element;
10365
10366       SCAN_PLAYFIELD(xx, yy)
10367       {
10368         if (Tile[xx][yy] == element)
10369         {
10370           if (reset_frame)
10371           {
10372             ResetGfxAnimation(xx, yy);
10373             ResetRandomAnimationValue(xx, yy);
10374           }
10375
10376           TEST_DrawLevelField(xx, yy);
10377         }
10378       }
10379
10380       break;
10381     }
10382
10383     // ---------- engine actions  ---------------------------------------------
10384
10385     case CA_SET_ENGINE_SCAN_MODE:
10386     {
10387       InitPlayfieldScanMode(action_arg);
10388
10389       break;
10390     }
10391
10392     default:
10393       break;
10394   }
10395 }
10396
10397 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10398 {
10399   int old_element = Tile[x][y];
10400   int new_element = GetElementFromGroupElement(element);
10401   int previous_move_direction = MovDir[x][y];
10402   int last_ce_value = CustomValue[x][y];
10403   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10404   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10405   boolean add_player_onto_element = (new_element_is_player &&
10406                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10407                                      IS_WALKABLE(old_element));
10408
10409   if (!add_player_onto_element)
10410   {
10411     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10412       RemoveMovingField(x, y);
10413     else
10414       RemoveField(x, y);
10415
10416     Tile[x][y] = new_element;
10417
10418     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10419       MovDir[x][y] = previous_move_direction;
10420
10421     if (element_info[new_element].use_last_ce_value)
10422       CustomValue[x][y] = last_ce_value;
10423
10424     InitField_WithBug1(x, y, FALSE);
10425
10426     new_element = Tile[x][y];   // element may have changed
10427
10428     ResetGfxAnimation(x, y);
10429     ResetRandomAnimationValue(x, y);
10430
10431     TEST_DrawLevelField(x, y);
10432
10433     if (GFX_CRUMBLED(new_element))
10434       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10435   }
10436
10437   // check if element under the player changes from accessible to unaccessible
10438   // (needed for special case of dropping element which then changes)
10439   // (must be checked after creating new element for walkable group elements)
10440   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10441       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10442   {
10443     Bang(x, y);
10444
10445     return;
10446   }
10447
10448   // "ChangeCount" not set yet to allow "entered by player" change one time
10449   if (new_element_is_player)
10450     RelocatePlayer(x, y, new_element);
10451
10452   if (is_change)
10453     ChangeCount[x][y]++;        // count number of changes in the same frame
10454
10455   TestIfBadThingTouchesPlayer(x, y);
10456   TestIfPlayerTouchesCustomElement(x, y);
10457   TestIfElementTouchesCustomElement(x, y);
10458 }
10459
10460 static void CreateField(int x, int y, int element)
10461 {
10462   CreateFieldExt(x, y, element, FALSE);
10463 }
10464
10465 static void CreateElementFromChange(int x, int y, int element)
10466 {
10467   element = GET_VALID_RUNTIME_ELEMENT(element);
10468
10469   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10470   {
10471     int old_element = Tile[x][y];
10472
10473     // prevent changed element from moving in same engine frame
10474     // unless both old and new element can either fall or move
10475     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10476         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10477       Stop[x][y] = TRUE;
10478   }
10479
10480   CreateFieldExt(x, y, element, TRUE);
10481 }
10482
10483 static boolean ChangeElement(int x, int y, int element, int page)
10484 {
10485   struct ElementInfo *ei = &element_info[element];
10486   struct ElementChangeInfo *change = &ei->change_page[page];
10487   int ce_value = CustomValue[x][y];
10488   int ce_score = ei->collect_score;
10489   int target_element;
10490   int old_element = Tile[x][y];
10491
10492   // always use default change event to prevent running into a loop
10493   if (ChangeEvent[x][y] == -1)
10494     ChangeEvent[x][y] = CE_DELAY;
10495
10496   if (ChangeEvent[x][y] == CE_DELAY)
10497   {
10498     // reset actual trigger element, trigger player and action element
10499     change->actual_trigger_element = EL_EMPTY;
10500     change->actual_trigger_player = EL_EMPTY;
10501     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10502     change->actual_trigger_side = CH_SIDE_NONE;
10503     change->actual_trigger_ce_value = 0;
10504     change->actual_trigger_ce_score = 0;
10505   }
10506
10507   // do not change elements more than a specified maximum number of changes
10508   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10509     return FALSE;
10510
10511   ChangeCount[x][y]++;          // count number of changes in the same frame
10512
10513   if (change->explode)
10514   {
10515     Bang(x, y);
10516
10517     return TRUE;
10518   }
10519
10520   if (change->use_target_content)
10521   {
10522     boolean complete_replace = TRUE;
10523     boolean can_replace[3][3];
10524     int xx, yy;
10525
10526     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10527     {
10528       boolean is_empty;
10529       boolean is_walkable;
10530       boolean is_diggable;
10531       boolean is_collectible;
10532       boolean is_removable;
10533       boolean is_destructible;
10534       int ex = x + xx - 1;
10535       int ey = y + yy - 1;
10536       int content_element = change->target_content.e[xx][yy];
10537       int e;
10538
10539       can_replace[xx][yy] = TRUE;
10540
10541       if (ex == x && ey == y)   // do not check changing element itself
10542         continue;
10543
10544       if (content_element == EL_EMPTY_SPACE)
10545       {
10546         can_replace[xx][yy] = FALSE;    // do not replace border with space
10547
10548         continue;
10549       }
10550
10551       if (!IN_LEV_FIELD(ex, ey))
10552       {
10553         can_replace[xx][yy] = FALSE;
10554         complete_replace = FALSE;
10555
10556         continue;
10557       }
10558
10559       e = Tile[ex][ey];
10560
10561       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10562         e = MovingOrBlocked2Element(ex, ey);
10563
10564       is_empty = (IS_FREE(ex, ey) ||
10565                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10566
10567       is_walkable     = (is_empty || IS_WALKABLE(e));
10568       is_diggable     = (is_empty || IS_DIGGABLE(e));
10569       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10570       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10571       is_removable    = (is_diggable || is_collectible);
10572
10573       can_replace[xx][yy] =
10574         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10575           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10576           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10577           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10578           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10579           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10580          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10581
10582       if (!can_replace[xx][yy])
10583         complete_replace = FALSE;
10584     }
10585
10586     if (!change->only_if_complete || complete_replace)
10587     {
10588       boolean something_has_changed = FALSE;
10589
10590       if (change->only_if_complete && change->use_random_replace &&
10591           RND(100) < change->random_percentage)
10592         return FALSE;
10593
10594       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10595       {
10596         int ex = x + xx - 1;
10597         int ey = y + yy - 1;
10598         int content_element;
10599
10600         if (can_replace[xx][yy] && (!change->use_random_replace ||
10601                                     RND(100) < change->random_percentage))
10602         {
10603           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10604             RemoveMovingField(ex, ey);
10605
10606           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10607
10608           content_element = change->target_content.e[xx][yy];
10609           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10610                                               ce_value, ce_score);
10611
10612           CreateElementFromChange(ex, ey, target_element);
10613
10614           something_has_changed = TRUE;
10615
10616           // for symmetry reasons, freeze newly created border elements
10617           if (ex != x || ey != y)
10618             Stop[ex][ey] = TRUE;        // no more moving in this frame
10619         }
10620       }
10621
10622       if (something_has_changed)
10623       {
10624         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10625         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10626       }
10627     }
10628   }
10629   else
10630   {
10631     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10632                                         ce_value, ce_score);
10633
10634     if (element == EL_DIAGONAL_GROWING ||
10635         element == EL_DIAGONAL_SHRINKING)
10636     {
10637       target_element = Store[x][y];
10638
10639       Store[x][y] = EL_EMPTY;
10640     }
10641
10642     CreateElementFromChange(x, y, target_element);
10643
10644     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10645     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10646   }
10647
10648   // this uses direct change before indirect change
10649   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10650
10651   return TRUE;
10652 }
10653
10654 static void HandleElementChange(int x, int y, int page)
10655 {
10656   int element = MovingOrBlocked2Element(x, y);
10657   struct ElementInfo *ei = &element_info[element];
10658   struct ElementChangeInfo *change = &ei->change_page[page];
10659   boolean handle_action_before_change = FALSE;
10660
10661 #ifdef DEBUG
10662   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10663       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10664   {
10665     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10666           x, y, element, element_info[element].token_name);
10667     Debug("game:playing:HandleElementChange", "This should never happen!");
10668   }
10669 #endif
10670
10671   // this can happen with classic bombs on walkable, changing elements
10672   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10673   {
10674     return;
10675   }
10676
10677   if (ChangeDelay[x][y] == 0)           // initialize element change
10678   {
10679     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10680
10681     if (change->can_change)
10682     {
10683       // !!! not clear why graphic animation should be reset at all here !!!
10684       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10685       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10686
10687       /*
10688         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10689
10690         When using an animation frame delay of 1 (this only happens with
10691         "sp_zonk.moving.left/right" in the classic graphics), the default
10692         (non-moving) animation shows wrong animation frames (while the
10693         moving animation, like "sp_zonk.moving.left/right", is correct,
10694         so this graphical bug never shows up with the classic graphics).
10695         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10696         be drawn instead of the correct frames 0,1,2,3. This is caused by
10697         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10698         an element change: First when the change delay ("ChangeDelay[][]")
10699         counter has reached zero after decrementing, then a second time in
10700         the next frame (after "GfxFrame[][]" was already incremented) when
10701         "ChangeDelay[][]" is reset to the initial delay value again.
10702
10703         This causes frame 0 to be drawn twice, while the last frame won't
10704         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10705
10706         As some animations may already be cleverly designed around this bug
10707         (at least the "Snake Bite" snake tail animation does this), it cannot
10708         simply be fixed here without breaking such existing animations.
10709         Unfortunately, it cannot easily be detected if a graphics set was
10710         designed "before" or "after" the bug was fixed. As a workaround,
10711         a new graphics set option "game.graphics_engine_version" was added
10712         to be able to specify the game's major release version for which the
10713         graphics set was designed, which can then be used to decide if the
10714         bugfix should be used (version 4 and above) or not (version 3 or
10715         below, or if no version was specified at all, as with old sets).
10716
10717         (The wrong/fixed animation frames can be tested with the test level set
10718         "test_gfxframe" and level "000", which contains a specially prepared
10719         custom element at level position (x/y) == (11/9) which uses the zonk
10720         animation mentioned above. Using "game.graphics_engine_version: 4"
10721         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10722         This can also be seen from the debug output for this test element.)
10723       */
10724
10725       // when a custom element is about to change (for example by change delay),
10726       // do not reset graphic animation when the custom element is moving
10727       if (game.graphics_engine_version < 4 &&
10728           !IS_MOVING(x, y))
10729       {
10730         ResetGfxAnimation(x, y);
10731         ResetRandomAnimationValue(x, y);
10732       }
10733
10734       if (change->pre_change_function)
10735         change->pre_change_function(x, y);
10736     }
10737   }
10738
10739   ChangeDelay[x][y]--;
10740
10741   if (ChangeDelay[x][y] != 0)           // continue element change
10742   {
10743     if (change->can_change)
10744     {
10745       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10746
10747       if (IS_ANIMATED(graphic))
10748         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10749
10750       if (change->change_function)
10751         change->change_function(x, y);
10752     }
10753   }
10754   else                                  // finish element change
10755   {
10756     if (ChangePage[x][y] != -1)         // remember page from delayed change
10757     {
10758       page = ChangePage[x][y];
10759       ChangePage[x][y] = -1;
10760
10761       change = &ei->change_page[page];
10762     }
10763
10764     if (IS_MOVING(x, y))                // never change a running system ;-)
10765     {
10766       ChangeDelay[x][y] = 1;            // try change after next move step
10767       ChangePage[x][y] = page;          // remember page to use for change
10768
10769       return;
10770     }
10771
10772     // special case: set new level random seed before changing element
10773     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10774       handle_action_before_change = TRUE;
10775
10776     if (change->has_action && handle_action_before_change)
10777       ExecuteCustomElementAction(x, y, element, page);
10778
10779     if (change->can_change)
10780     {
10781       if (ChangeElement(x, y, element, page))
10782       {
10783         if (change->post_change_function)
10784           change->post_change_function(x, y);
10785       }
10786     }
10787
10788     if (change->has_action && !handle_action_before_change)
10789       ExecuteCustomElementAction(x, y, element, page);
10790   }
10791 }
10792
10793 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10794                                               int trigger_element,
10795                                               int trigger_event,
10796                                               int trigger_player,
10797                                               int trigger_side,
10798                                               int trigger_page)
10799 {
10800   boolean change_done_any = FALSE;
10801   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10802   int i;
10803
10804   if (!(trigger_events[trigger_element][trigger_event]))
10805     return FALSE;
10806
10807   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10808
10809   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10810   {
10811     int element = EL_CUSTOM_START + i;
10812     boolean change_done = FALSE;
10813     int p;
10814
10815     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10816         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10817       continue;
10818
10819     for (p = 0; p < element_info[element].num_change_pages; p++)
10820     {
10821       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10822
10823       if (change->can_change_or_has_action &&
10824           change->has_event[trigger_event] &&
10825           change->trigger_side & trigger_side &&
10826           change->trigger_player & trigger_player &&
10827           change->trigger_page & trigger_page_bits &&
10828           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10829       {
10830         change->actual_trigger_element = trigger_element;
10831         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10832         change->actual_trigger_player_bits = trigger_player;
10833         change->actual_trigger_side = trigger_side;
10834         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10835         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10836
10837         if ((change->can_change && !change_done) || change->has_action)
10838         {
10839           int x, y;
10840
10841           SCAN_PLAYFIELD(x, y)
10842           {
10843             if (Tile[x][y] == element)
10844             {
10845               if (change->can_change && !change_done)
10846               {
10847                 // if element already changed in this frame, not only prevent
10848                 // another element change (checked in ChangeElement()), but
10849                 // also prevent additional element actions for this element
10850
10851                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10852                     !level.use_action_after_change_bug)
10853                   continue;
10854
10855                 ChangeDelay[x][y] = 1;
10856                 ChangeEvent[x][y] = trigger_event;
10857
10858                 HandleElementChange(x, y, p);
10859               }
10860               else if (change->has_action)
10861               {
10862                 // if element already changed in this frame, not only prevent
10863                 // another element change (checked in ChangeElement()), but
10864                 // also prevent additional element actions for this element
10865
10866                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10867                     !level.use_action_after_change_bug)
10868                   continue;
10869
10870                 ExecuteCustomElementAction(x, y, element, p);
10871                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10872               }
10873             }
10874           }
10875
10876           if (change->can_change)
10877           {
10878             change_done = TRUE;
10879             change_done_any = TRUE;
10880           }
10881         }
10882       }
10883     }
10884   }
10885
10886   RECURSION_LOOP_DETECTION_END();
10887
10888   return change_done_any;
10889 }
10890
10891 static boolean CheckElementChangeExt(int x, int y,
10892                                      int element,
10893                                      int trigger_element,
10894                                      int trigger_event,
10895                                      int trigger_player,
10896                                      int trigger_side)
10897 {
10898   boolean change_done = FALSE;
10899   int p;
10900
10901   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10902       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10903     return FALSE;
10904
10905   if (Tile[x][y] == EL_BLOCKED)
10906   {
10907     Blocked2Moving(x, y, &x, &y);
10908     element = Tile[x][y];
10909   }
10910
10911   // check if element has already changed or is about to change after moving
10912   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10913        Tile[x][y] != element) ||
10914
10915       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10916        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10917         ChangePage[x][y] != -1)))
10918     return FALSE;
10919
10920   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10921
10922   for (p = 0; p < element_info[element].num_change_pages; p++)
10923   {
10924     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10925
10926     /* check trigger element for all events where the element that is checked
10927        for changing interacts with a directly adjacent element -- this is
10928        different to element changes that affect other elements to change on the
10929        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10930     boolean check_trigger_element =
10931       (trigger_event == CE_TOUCHING_X ||
10932        trigger_event == CE_HITTING_X ||
10933        trigger_event == CE_HIT_BY_X ||
10934        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
10935
10936     if (change->can_change_or_has_action &&
10937         change->has_event[trigger_event] &&
10938         change->trigger_side & trigger_side &&
10939         change->trigger_player & trigger_player &&
10940         (!check_trigger_element ||
10941          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10942     {
10943       change->actual_trigger_element = trigger_element;
10944       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10945       change->actual_trigger_player_bits = trigger_player;
10946       change->actual_trigger_side = trigger_side;
10947       change->actual_trigger_ce_value = CustomValue[x][y];
10948       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10949
10950       // special case: trigger element not at (x,y) position for some events
10951       if (check_trigger_element)
10952       {
10953         static struct
10954         {
10955           int dx, dy;
10956         } move_xy[] =
10957           {
10958             {  0,  0 },
10959             { -1,  0 },
10960             { +1,  0 },
10961             {  0,  0 },
10962             {  0, -1 },
10963             {  0,  0 }, { 0, 0 }, { 0, 0 },
10964             {  0, +1 }
10965           };
10966
10967         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10968         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10969
10970         change->actual_trigger_ce_value = CustomValue[xx][yy];
10971         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10972       }
10973
10974       if (change->can_change && !change_done)
10975       {
10976         ChangeDelay[x][y] = 1;
10977         ChangeEvent[x][y] = trigger_event;
10978
10979         HandleElementChange(x, y, p);
10980
10981         change_done = TRUE;
10982       }
10983       else if (change->has_action)
10984       {
10985         ExecuteCustomElementAction(x, y, element, p);
10986         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10987       }
10988     }
10989   }
10990
10991   RECURSION_LOOP_DETECTION_END();
10992
10993   return change_done;
10994 }
10995
10996 static void PlayPlayerSound(struct PlayerInfo *player)
10997 {
10998   int jx = player->jx, jy = player->jy;
10999   int sound_element = player->artwork_element;
11000   int last_action = player->last_action_waiting;
11001   int action = player->action_waiting;
11002
11003   if (player->is_waiting)
11004   {
11005     if (action != last_action)
11006       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11007     else
11008       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11009   }
11010   else
11011   {
11012     if (action != last_action)
11013       StopSound(element_info[sound_element].sound[last_action]);
11014
11015     if (last_action == ACTION_SLEEPING)
11016       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11017   }
11018 }
11019
11020 static void PlayAllPlayersSound(void)
11021 {
11022   int i;
11023
11024   for (i = 0; i < MAX_PLAYERS; i++)
11025     if (stored_player[i].active)
11026       PlayPlayerSound(&stored_player[i]);
11027 }
11028
11029 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11030 {
11031   boolean last_waiting = player->is_waiting;
11032   int move_dir = player->MovDir;
11033
11034   player->dir_waiting = move_dir;
11035   player->last_action_waiting = player->action_waiting;
11036
11037   if (is_waiting)
11038   {
11039     if (!last_waiting)          // not waiting -> waiting
11040     {
11041       player->is_waiting = TRUE;
11042
11043       player->frame_counter_bored =
11044         FrameCounter +
11045         game.player_boring_delay_fixed +
11046         GetSimpleRandom(game.player_boring_delay_random);
11047       player->frame_counter_sleeping =
11048         FrameCounter +
11049         game.player_sleeping_delay_fixed +
11050         GetSimpleRandom(game.player_sleeping_delay_random);
11051
11052       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11053     }
11054
11055     if (game.player_sleeping_delay_fixed +
11056         game.player_sleeping_delay_random > 0 &&
11057         player->anim_delay_counter == 0 &&
11058         player->post_delay_counter == 0 &&
11059         FrameCounter >= player->frame_counter_sleeping)
11060       player->is_sleeping = TRUE;
11061     else if (game.player_boring_delay_fixed +
11062              game.player_boring_delay_random > 0 &&
11063              FrameCounter >= player->frame_counter_bored)
11064       player->is_bored = TRUE;
11065
11066     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11067                               player->is_bored ? ACTION_BORING :
11068                               ACTION_WAITING);
11069
11070     if (player->is_sleeping && player->use_murphy)
11071     {
11072       // special case for sleeping Murphy when leaning against non-free tile
11073
11074       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11075           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11076            !IS_MOVING(player->jx - 1, player->jy)))
11077         move_dir = MV_LEFT;
11078       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11079                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11080                 !IS_MOVING(player->jx + 1, player->jy)))
11081         move_dir = MV_RIGHT;
11082       else
11083         player->is_sleeping = FALSE;
11084
11085       player->dir_waiting = move_dir;
11086     }
11087
11088     if (player->is_sleeping)
11089     {
11090       if (player->num_special_action_sleeping > 0)
11091       {
11092         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11093         {
11094           int last_special_action = player->special_action_sleeping;
11095           int num_special_action = player->num_special_action_sleeping;
11096           int special_action =
11097             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11098              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11099              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11100              last_special_action + 1 : ACTION_SLEEPING);
11101           int special_graphic =
11102             el_act_dir2img(player->artwork_element, special_action, move_dir);
11103
11104           player->anim_delay_counter =
11105             graphic_info[special_graphic].anim_delay_fixed +
11106             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11107           player->post_delay_counter =
11108             graphic_info[special_graphic].post_delay_fixed +
11109             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11110
11111           player->special_action_sleeping = special_action;
11112         }
11113
11114         if (player->anim_delay_counter > 0)
11115         {
11116           player->action_waiting = player->special_action_sleeping;
11117           player->anim_delay_counter--;
11118         }
11119         else if (player->post_delay_counter > 0)
11120         {
11121           player->post_delay_counter--;
11122         }
11123       }
11124     }
11125     else if (player->is_bored)
11126     {
11127       if (player->num_special_action_bored > 0)
11128       {
11129         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11130         {
11131           int special_action =
11132             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11133           int special_graphic =
11134             el_act_dir2img(player->artwork_element, special_action, move_dir);
11135
11136           player->anim_delay_counter =
11137             graphic_info[special_graphic].anim_delay_fixed +
11138             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11139           player->post_delay_counter =
11140             graphic_info[special_graphic].post_delay_fixed +
11141             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11142
11143           player->special_action_bored = special_action;
11144         }
11145
11146         if (player->anim_delay_counter > 0)
11147         {
11148           player->action_waiting = player->special_action_bored;
11149           player->anim_delay_counter--;
11150         }
11151         else if (player->post_delay_counter > 0)
11152         {
11153           player->post_delay_counter--;
11154         }
11155       }
11156     }
11157   }
11158   else if (last_waiting)        // waiting -> not waiting
11159   {
11160     player->is_waiting = FALSE;
11161     player->is_bored = FALSE;
11162     player->is_sleeping = FALSE;
11163
11164     player->frame_counter_bored = -1;
11165     player->frame_counter_sleeping = -1;
11166
11167     player->anim_delay_counter = 0;
11168     player->post_delay_counter = 0;
11169
11170     player->dir_waiting = player->MovDir;
11171     player->action_waiting = ACTION_DEFAULT;
11172
11173     player->special_action_bored = ACTION_DEFAULT;
11174     player->special_action_sleeping = ACTION_DEFAULT;
11175   }
11176 }
11177
11178 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11179 {
11180   if ((!player->is_moving  && player->was_moving) ||
11181       (player->MovPos == 0 && player->was_moving) ||
11182       (player->is_snapping && !player->was_snapping) ||
11183       (player->is_dropping && !player->was_dropping))
11184   {
11185     if (!CheckSaveEngineSnapshotToList())
11186       return;
11187
11188     player->was_moving = FALSE;
11189     player->was_snapping = TRUE;
11190     player->was_dropping = TRUE;
11191   }
11192   else
11193   {
11194     if (player->is_moving)
11195       player->was_moving = TRUE;
11196
11197     if (!player->is_snapping)
11198       player->was_snapping = FALSE;
11199
11200     if (!player->is_dropping)
11201       player->was_dropping = FALSE;
11202   }
11203 }
11204
11205 static void CheckSingleStepMode(struct PlayerInfo *player)
11206 {
11207   if (tape.single_step && tape.recording && !tape.pausing)
11208   {
11209     /* as it is called "single step mode", just return to pause mode when the
11210        player stopped moving after one tile (or never starts moving at all) */
11211     if (!player->is_moving &&
11212         !player->is_pushing &&
11213         !player->is_dropping_pressed)
11214       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11215   }
11216
11217   CheckSaveEngineSnapshot(player);
11218 }
11219
11220 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11221 {
11222   int left      = player_action & JOY_LEFT;
11223   int right     = player_action & JOY_RIGHT;
11224   int up        = player_action & JOY_UP;
11225   int down      = player_action & JOY_DOWN;
11226   int button1   = player_action & JOY_BUTTON_1;
11227   int button2   = player_action & JOY_BUTTON_2;
11228   int dx        = (left ? -1 : right ? 1 : 0);
11229   int dy        = (up   ? -1 : down  ? 1 : 0);
11230
11231   if (!player->active || tape.pausing)
11232     return 0;
11233
11234   if (player_action)
11235   {
11236     if (button1)
11237       SnapField(player, dx, dy);
11238     else
11239     {
11240       if (button2)
11241         DropElement(player);
11242
11243       MovePlayer(player, dx, dy);
11244     }
11245
11246     CheckSingleStepMode(player);
11247
11248     SetPlayerWaiting(player, FALSE);
11249
11250     return player_action;
11251   }
11252   else
11253   {
11254     // no actions for this player (no input at player's configured device)
11255
11256     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11257     SnapField(player, 0, 0);
11258     CheckGravityMovementWhenNotMoving(player);
11259
11260     if (player->MovPos == 0)
11261       SetPlayerWaiting(player, TRUE);
11262
11263     if (player->MovPos == 0)    // needed for tape.playing
11264       player->is_moving = FALSE;
11265
11266     player->is_dropping = FALSE;
11267     player->is_dropping_pressed = FALSE;
11268     player->drop_pressed_delay = 0;
11269
11270     CheckSingleStepMode(player);
11271
11272     return 0;
11273   }
11274 }
11275
11276 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11277                                          byte *tape_action)
11278 {
11279   if (!tape.use_mouse_actions)
11280     return;
11281
11282   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11283   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11284   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11285 }
11286
11287 static void SetTapeActionFromMouseAction(byte *tape_action,
11288                                          struct MouseActionInfo *mouse_action)
11289 {
11290   if (!tape.use_mouse_actions)
11291     return;
11292
11293   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11294   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11295   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11296 }
11297
11298 static void CheckLevelSolved(void)
11299 {
11300   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11301   {
11302     if (game_em.level_solved &&
11303         !game_em.game_over)                             // game won
11304     {
11305       LevelSolved();
11306
11307       game_em.game_over = TRUE;
11308
11309       game.all_players_gone = TRUE;
11310     }
11311
11312     if (game_em.game_over)                              // game lost
11313       game.all_players_gone = TRUE;
11314   }
11315   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11316   {
11317     if (game_sp.level_solved &&
11318         !game_sp.game_over)                             // game won
11319     {
11320       LevelSolved();
11321
11322       game_sp.game_over = TRUE;
11323
11324       game.all_players_gone = TRUE;
11325     }
11326
11327     if (game_sp.game_over)                              // game lost
11328       game.all_players_gone = TRUE;
11329   }
11330   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11331   {
11332     if (game_mm.level_solved &&
11333         !game_mm.game_over)                             // game won
11334     {
11335       LevelSolved();
11336
11337       game_mm.game_over = TRUE;
11338
11339       game.all_players_gone = TRUE;
11340     }
11341
11342     if (game_mm.game_over)                              // game lost
11343       game.all_players_gone = TRUE;
11344   }
11345 }
11346
11347 static void CheckLevelTime(void)
11348 {
11349   int i;
11350
11351   if (TimeFrames >= FRAMES_PER_SECOND)
11352   {
11353     TimeFrames = 0;
11354     TapeTime++;
11355
11356     for (i = 0; i < MAX_PLAYERS; i++)
11357     {
11358       struct PlayerInfo *player = &stored_player[i];
11359
11360       if (SHIELD_ON(player))
11361       {
11362         player->shield_normal_time_left--;
11363
11364         if (player->shield_deadly_time_left > 0)
11365           player->shield_deadly_time_left--;
11366       }
11367     }
11368
11369     if (!game.LevelSolved && !level.use_step_counter)
11370     {
11371       TimePlayed++;
11372
11373       if (TimeLeft > 0)
11374       {
11375         TimeLeft--;
11376
11377         if (TimeLeft <= 10 && setup.time_limit)
11378           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11379
11380         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11381            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11382
11383         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11384
11385         if (!TimeLeft && setup.time_limit)
11386         {
11387           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11388             game_em.lev->killed_out_of_time = TRUE;
11389           else
11390             for (i = 0; i < MAX_PLAYERS; i++)
11391               KillPlayer(&stored_player[i]);
11392         }
11393       }
11394       else if (game.no_time_limit && !game.all_players_gone)
11395       {
11396         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11397       }
11398
11399       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11400     }
11401
11402     if (tape.recording || tape.playing)
11403       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11404   }
11405
11406   if (tape.recording || tape.playing)
11407     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11408
11409   UpdateAndDisplayGameControlValues();
11410 }
11411
11412 void AdvanceFrameAndPlayerCounters(int player_nr)
11413 {
11414   int i;
11415
11416   // advance frame counters (global frame counter and time frame counter)
11417   FrameCounter++;
11418   TimeFrames++;
11419
11420   // advance player counters (counters for move delay, move animation etc.)
11421   for (i = 0; i < MAX_PLAYERS; i++)
11422   {
11423     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11424     int move_delay_value = stored_player[i].move_delay_value;
11425     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11426
11427     if (!advance_player_counters)       // not all players may be affected
11428       continue;
11429
11430     if (move_frames == 0)       // less than one move per game frame
11431     {
11432       int stepsize = TILEX / move_delay_value;
11433       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11434       int count = (stored_player[i].is_moving ?
11435                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11436
11437       if (count % delay == 0)
11438         move_frames = 1;
11439     }
11440
11441     stored_player[i].Frame += move_frames;
11442
11443     if (stored_player[i].MovPos != 0)
11444       stored_player[i].StepFrame += move_frames;
11445
11446     if (stored_player[i].move_delay > 0)
11447       stored_player[i].move_delay--;
11448
11449     // due to bugs in previous versions, counter must count up, not down
11450     if (stored_player[i].push_delay != -1)
11451       stored_player[i].push_delay++;
11452
11453     if (stored_player[i].drop_delay > 0)
11454       stored_player[i].drop_delay--;
11455
11456     if (stored_player[i].is_dropping_pressed)
11457       stored_player[i].drop_pressed_delay++;
11458   }
11459 }
11460
11461 void StartGameActions(boolean init_network_game, boolean record_tape,
11462                       int random_seed)
11463 {
11464   unsigned int new_random_seed = InitRND(random_seed);
11465
11466   if (record_tape)
11467     TapeStartRecording(new_random_seed);
11468
11469   if (init_network_game)
11470   {
11471     SendToServer_LevelFile();
11472     SendToServer_StartPlaying();
11473
11474     return;
11475   }
11476
11477   InitGame();
11478 }
11479
11480 static void GameActionsExt(void)
11481 {
11482 #if 0
11483   static unsigned int game_frame_delay = 0;
11484 #endif
11485   unsigned int game_frame_delay_value;
11486   byte *recorded_player_action;
11487   byte summarized_player_action = 0;
11488   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11489   int i;
11490
11491   // detect endless loops, caused by custom element programming
11492   if (recursion_loop_detected && recursion_loop_depth == 0)
11493   {
11494     char *message = getStringCat3("Internal Error! Element ",
11495                                   EL_NAME(recursion_loop_element),
11496                                   " caused endless loop! Quit the game?");
11497
11498     Warn("element '%s' caused endless loop in game engine",
11499          EL_NAME(recursion_loop_element));
11500
11501     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11502
11503     recursion_loop_detected = FALSE;    // if game should be continued
11504
11505     free(message);
11506
11507     return;
11508   }
11509
11510   if (game.restart_level)
11511     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11512
11513   CheckLevelSolved();
11514
11515   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11516     GameWon();
11517
11518   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11519     TapeStop();
11520
11521   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11522     return;
11523
11524   game_frame_delay_value =
11525     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11526
11527   if (tape.playing && tape.warp_forward && !tape.pausing)
11528     game_frame_delay_value = 0;
11529
11530   SetVideoFrameDelay(game_frame_delay_value);
11531
11532   // (de)activate virtual buttons depending on current game status
11533   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11534   {
11535     if (game.all_players_gone)  // if no players there to be controlled anymore
11536       SetOverlayActive(FALSE);
11537     else if (!tape.playing)     // if game continues after tape stopped playing
11538       SetOverlayActive(TRUE);
11539   }
11540
11541 #if 0
11542 #if 0
11543   // ---------- main game synchronization point ----------
11544
11545   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11546
11547   Debug("game:playing:skip", "skip == %d", skip);
11548
11549 #else
11550   // ---------- main game synchronization point ----------
11551
11552   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11553 #endif
11554 #endif
11555
11556   if (network_playing && !network_player_action_received)
11557   {
11558     // try to get network player actions in time
11559
11560     // last chance to get network player actions without main loop delay
11561     HandleNetworking();
11562
11563     // game was quit by network peer
11564     if (game_status != GAME_MODE_PLAYING)
11565       return;
11566
11567     // check if network player actions still missing and game still running
11568     if (!network_player_action_received && !checkGameEnded())
11569       return;           // failed to get network player actions in time
11570
11571     // do not yet reset "network_player_action_received" (for tape.pausing)
11572   }
11573
11574   if (tape.pausing)
11575     return;
11576
11577   // at this point we know that we really continue executing the game
11578
11579   network_player_action_received = FALSE;
11580
11581   // when playing tape, read previously recorded player input from tape data
11582   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11583
11584   local_player->effective_mouse_action = local_player->mouse_action;
11585
11586   if (recorded_player_action != NULL)
11587     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11588                                  recorded_player_action);
11589
11590   // TapePlayAction() may return NULL when toggling to "pause before death"
11591   if (tape.pausing)
11592     return;
11593
11594   if (tape.set_centered_player)
11595   {
11596     game.centered_player_nr_next = tape.centered_player_nr_next;
11597     game.set_centered_player = TRUE;
11598   }
11599
11600   for (i = 0; i < MAX_PLAYERS; i++)
11601   {
11602     summarized_player_action |= stored_player[i].action;
11603
11604     if (!network_playing && (game.team_mode || tape.playing))
11605       stored_player[i].effective_action = stored_player[i].action;
11606   }
11607
11608   if (network_playing && !checkGameEnded())
11609     SendToServer_MovePlayer(summarized_player_action);
11610
11611   // summarize all actions at local players mapped input device position
11612   // (this allows using different input devices in single player mode)
11613   if (!network.enabled && !game.team_mode)
11614     stored_player[map_player_action[local_player->index_nr]].effective_action =
11615       summarized_player_action;
11616
11617   // summarize all actions at centered player in local team mode
11618   if (tape.recording &&
11619       setup.team_mode && !network.enabled &&
11620       setup.input_on_focus &&
11621       game.centered_player_nr != -1)
11622   {
11623     for (i = 0; i < MAX_PLAYERS; i++)
11624       stored_player[map_player_action[i]].effective_action =
11625         (i == game.centered_player_nr ? summarized_player_action : 0);
11626   }
11627
11628   if (recorded_player_action != NULL)
11629     for (i = 0; i < MAX_PLAYERS; i++)
11630       stored_player[i].effective_action = recorded_player_action[i];
11631
11632   for (i = 0; i < MAX_PLAYERS; i++)
11633   {
11634     tape_action[i] = stored_player[i].effective_action;
11635
11636     /* (this may happen in the RND game engine if a player was not present on
11637        the playfield on level start, but appeared later from a custom element */
11638     if (setup.team_mode &&
11639         tape.recording &&
11640         tape_action[i] &&
11641         !tape.player_participates[i])
11642       tape.player_participates[i] = TRUE;
11643   }
11644
11645   SetTapeActionFromMouseAction(tape_action,
11646                                &local_player->effective_mouse_action);
11647
11648   // only record actions from input devices, but not programmed actions
11649   if (tape.recording)
11650     TapeRecordAction(tape_action);
11651
11652   // remember if game was played (especially after tape stopped playing)
11653   if (!tape.playing && summarized_player_action)
11654     game.GamePlayed = TRUE;
11655
11656 #if USE_NEW_PLAYER_ASSIGNMENTS
11657   // !!! also map player actions in single player mode !!!
11658   // if (game.team_mode)
11659   if (1)
11660   {
11661     byte mapped_action[MAX_PLAYERS];
11662
11663 #if DEBUG_PLAYER_ACTIONS
11664     Print(":::");
11665     for (i = 0; i < MAX_PLAYERS; i++)
11666       Print(" %d, ", stored_player[i].effective_action);
11667 #endif
11668
11669     for (i = 0; i < MAX_PLAYERS; i++)
11670       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11671
11672     for (i = 0; i < MAX_PLAYERS; i++)
11673       stored_player[i].effective_action = mapped_action[i];
11674
11675 #if DEBUG_PLAYER_ACTIONS
11676     Print(" =>");
11677     for (i = 0; i < MAX_PLAYERS; i++)
11678       Print(" %d, ", stored_player[i].effective_action);
11679     Print("\n");
11680 #endif
11681   }
11682 #if DEBUG_PLAYER_ACTIONS
11683   else
11684   {
11685     Print(":::");
11686     for (i = 0; i < MAX_PLAYERS; i++)
11687       Print(" %d, ", stored_player[i].effective_action);
11688     Print("\n");
11689   }
11690 #endif
11691 #endif
11692
11693   for (i = 0; i < MAX_PLAYERS; i++)
11694   {
11695     // allow engine snapshot in case of changed movement attempt
11696     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11697         (stored_player[i].effective_action & KEY_MOTION))
11698       game.snapshot.changed_action = TRUE;
11699
11700     // allow engine snapshot in case of snapping/dropping attempt
11701     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11702         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11703       game.snapshot.changed_action = TRUE;
11704
11705     game.snapshot.last_action[i] = stored_player[i].effective_action;
11706   }
11707
11708   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11709   {
11710     GameActions_EM_Main();
11711   }
11712   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11713   {
11714     GameActions_SP_Main();
11715   }
11716   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11717   {
11718     GameActions_MM_Main();
11719   }
11720   else
11721   {
11722     GameActions_RND_Main();
11723   }
11724
11725   BlitScreenToBitmap(backbuffer);
11726
11727   CheckLevelSolved();
11728   CheckLevelTime();
11729
11730   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11731
11732   if (global.show_frames_per_second)
11733   {
11734     static unsigned int fps_counter = 0;
11735     static int fps_frames = 0;
11736     unsigned int fps_delay_ms = Counter() - fps_counter;
11737
11738     fps_frames++;
11739
11740     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11741     {
11742       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11743
11744       fps_frames = 0;
11745       fps_counter = Counter();
11746
11747       // always draw FPS to screen after FPS value was updated
11748       redraw_mask |= REDRAW_FPS;
11749     }
11750
11751     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11752     if (GetDrawDeactivationMask() == REDRAW_NONE)
11753       redraw_mask |= REDRAW_FPS;
11754   }
11755 }
11756
11757 static void GameActions_CheckSaveEngineSnapshot(void)
11758 {
11759   if (!game.snapshot.save_snapshot)
11760     return;
11761
11762   // clear flag for saving snapshot _before_ saving snapshot
11763   game.snapshot.save_snapshot = FALSE;
11764
11765   SaveEngineSnapshotToList();
11766 }
11767
11768 void GameActions(void)
11769 {
11770   GameActionsExt();
11771
11772   GameActions_CheckSaveEngineSnapshot();
11773 }
11774
11775 void GameActions_EM_Main(void)
11776 {
11777   byte effective_action[MAX_PLAYERS];
11778   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11779   int i;
11780
11781   for (i = 0; i < MAX_PLAYERS; i++)
11782     effective_action[i] = stored_player[i].effective_action;
11783
11784   GameActions_EM(effective_action, warp_mode);
11785 }
11786
11787 void GameActions_SP_Main(void)
11788 {
11789   byte effective_action[MAX_PLAYERS];
11790   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11791   int i;
11792
11793   for (i = 0; i < MAX_PLAYERS; i++)
11794     effective_action[i] = stored_player[i].effective_action;
11795
11796   GameActions_SP(effective_action, warp_mode);
11797
11798   for (i = 0; i < MAX_PLAYERS; i++)
11799   {
11800     if (stored_player[i].force_dropping)
11801       stored_player[i].action |= KEY_BUTTON_DROP;
11802
11803     stored_player[i].force_dropping = FALSE;
11804   }
11805 }
11806
11807 void GameActions_MM_Main(void)
11808 {
11809   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11810
11811   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11812 }
11813
11814 void GameActions_RND_Main(void)
11815 {
11816   GameActions_RND();
11817 }
11818
11819 void GameActions_RND(void)
11820 {
11821   static struct MouseActionInfo mouse_action_last = { 0 };
11822   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
11823   int magic_wall_x = 0, magic_wall_y = 0;
11824   int i, x, y, element, graphic, last_gfx_frame;
11825
11826   InitPlayfieldScanModeVars();
11827
11828   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11829   {
11830     SCAN_PLAYFIELD(x, y)
11831     {
11832       ChangeCount[x][y] = 0;
11833       ChangeEvent[x][y] = -1;
11834     }
11835   }
11836
11837   if (game.set_centered_player)
11838   {
11839     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11840
11841     // switching to "all players" only possible if all players fit to screen
11842     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11843     {
11844       game.centered_player_nr_next = game.centered_player_nr;
11845       game.set_centered_player = FALSE;
11846     }
11847
11848     // do not switch focus to non-existing (or non-active) player
11849     if (game.centered_player_nr_next >= 0 &&
11850         !stored_player[game.centered_player_nr_next].active)
11851     {
11852       game.centered_player_nr_next = game.centered_player_nr;
11853       game.set_centered_player = FALSE;
11854     }
11855   }
11856
11857   if (game.set_centered_player &&
11858       ScreenMovPos == 0)        // screen currently aligned at tile position
11859   {
11860     int sx, sy;
11861
11862     if (game.centered_player_nr_next == -1)
11863     {
11864       setScreenCenteredToAllPlayers(&sx, &sy);
11865     }
11866     else
11867     {
11868       sx = stored_player[game.centered_player_nr_next].jx;
11869       sy = stored_player[game.centered_player_nr_next].jy;
11870     }
11871
11872     game.centered_player_nr = game.centered_player_nr_next;
11873     game.set_centered_player = FALSE;
11874
11875     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11876     DrawGameDoorValues();
11877   }
11878
11879   for (i = 0; i < MAX_PLAYERS; i++)
11880   {
11881     int actual_player_action = stored_player[i].effective_action;
11882
11883 #if 1
11884     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11885        - rnd_equinox_tetrachloride 048
11886        - rnd_equinox_tetrachloride_ii 096
11887        - rnd_emanuel_schmieg 002
11888        - doctor_sloan_ww 001, 020
11889     */
11890     if (stored_player[i].MovPos == 0)
11891       CheckGravityMovement(&stored_player[i]);
11892 #endif
11893
11894     // overwrite programmed action with tape action
11895     if (stored_player[i].programmed_action)
11896       actual_player_action = stored_player[i].programmed_action;
11897
11898     PlayerActions(&stored_player[i], actual_player_action);
11899
11900     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11901   }
11902
11903   ScrollScreen(NULL, SCROLL_GO_ON);
11904
11905   /* for backwards compatibility, the following code emulates a fixed bug that
11906      occured when pushing elements (causing elements that just made their last
11907      pushing step to already (if possible) make their first falling step in the
11908      same game frame, which is bad); this code is also needed to use the famous
11909      "spring push bug" which is used in older levels and might be wanted to be
11910      used also in newer levels, but in this case the buggy pushing code is only
11911      affecting the "spring" element and no other elements */
11912
11913   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11914   {
11915     for (i = 0; i < MAX_PLAYERS; i++)
11916     {
11917       struct PlayerInfo *player = &stored_player[i];
11918       int x = player->jx;
11919       int y = player->jy;
11920
11921       if (player->active && player->is_pushing && player->is_moving &&
11922           IS_MOVING(x, y) &&
11923           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11924            Tile[x][y] == EL_SPRING))
11925       {
11926         ContinueMoving(x, y);
11927
11928         // continue moving after pushing (this is actually a bug)
11929         if (!IS_MOVING(x, y))
11930           Stop[x][y] = FALSE;
11931       }
11932     }
11933   }
11934
11935   SCAN_PLAYFIELD(x, y)
11936   {
11937     Last[x][y] = Tile[x][y];
11938
11939     ChangeCount[x][y] = 0;
11940     ChangeEvent[x][y] = -1;
11941
11942     // this must be handled before main playfield loop
11943     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
11944     {
11945       MovDelay[x][y]--;
11946       if (MovDelay[x][y] <= 0)
11947         RemoveField(x, y);
11948     }
11949
11950     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
11951     {
11952       MovDelay[x][y]--;
11953       if (MovDelay[x][y] <= 0)
11954       {
11955         RemoveField(x, y);
11956         TEST_DrawLevelField(x, y);
11957
11958         TestIfElementTouchesCustomElement(x, y);        // for empty space
11959       }
11960     }
11961
11962 #if DEBUG
11963     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11964     {
11965       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
11966             x, y);
11967       Debug("game:playing:GameActions_RND", "This should never happen!");
11968
11969       ChangePage[x][y] = -1;
11970     }
11971 #endif
11972
11973     Stop[x][y] = FALSE;
11974     if (WasJustMoving[x][y] > 0)
11975       WasJustMoving[x][y]--;
11976     if (WasJustFalling[x][y] > 0)
11977       WasJustFalling[x][y]--;
11978     if (CheckCollision[x][y] > 0)
11979       CheckCollision[x][y]--;
11980     if (CheckImpact[x][y] > 0)
11981       CheckImpact[x][y]--;
11982
11983     GfxFrame[x][y]++;
11984
11985     /* reset finished pushing action (not done in ContinueMoving() to allow
11986        continuous pushing animation for elements with zero push delay) */
11987     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11988     {
11989       ResetGfxAnimation(x, y);
11990       TEST_DrawLevelField(x, y);
11991     }
11992
11993 #if DEBUG
11994     if (IS_BLOCKED(x, y))
11995     {
11996       int oldx, oldy;
11997
11998       Blocked2Moving(x, y, &oldx, &oldy);
11999       if (!IS_MOVING(oldx, oldy))
12000       {
12001         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12002         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12003         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12004         Debug("game:playing:GameActions_RND", "This should never happen!");
12005       }
12006     }
12007 #endif
12008   }
12009
12010   if (mouse_action.button)
12011   {
12012     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12013
12014     x = mouse_action.lx;
12015     y = mouse_action.ly;
12016     element = Tile[x][y];
12017
12018     if (new_button)
12019     {
12020       CheckElementChange(x, y, element, EL_UNDEFINED, CE_CLICKED_BY_MOUSE);
12021       CheckTriggeredElementChange(x, y, element, CE_MOUSE_CLICKED_ON_X);
12022     }
12023
12024     CheckElementChange(x, y, element, EL_UNDEFINED, CE_PRESSED_BY_MOUSE);
12025     CheckTriggeredElementChange(x, y, element, CE_MOUSE_PRESSED_ON_X);
12026   }
12027
12028   SCAN_PLAYFIELD(x, y)
12029   {
12030     element = Tile[x][y];
12031     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12032     last_gfx_frame = GfxFrame[x][y];
12033
12034     ResetGfxFrame(x, y);
12035
12036     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12037       DrawLevelGraphicAnimation(x, y, graphic);
12038
12039     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12040         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12041       ResetRandomAnimationValue(x, y);
12042
12043     SetRandomAnimationValue(x, y);
12044
12045     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12046
12047     if (IS_INACTIVE(element))
12048     {
12049       if (IS_ANIMATED(graphic))
12050         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12051
12052       continue;
12053     }
12054
12055     // this may take place after moving, so 'element' may have changed
12056     if (IS_CHANGING(x, y) &&
12057         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12058     {
12059       int page = element_info[element].event_page_nr[CE_DELAY];
12060
12061       HandleElementChange(x, y, page);
12062
12063       element = Tile[x][y];
12064       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12065     }
12066
12067     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12068     {
12069       StartMoving(x, y);
12070
12071       element = Tile[x][y];
12072       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12073
12074       if (IS_ANIMATED(graphic) &&
12075           !IS_MOVING(x, y) &&
12076           !Stop[x][y])
12077         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12078
12079       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12080         TEST_DrawTwinkleOnField(x, y);
12081     }
12082     else if (element == EL_ACID)
12083     {
12084       if (!Stop[x][y])
12085         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12086     }
12087     else if ((element == EL_EXIT_OPEN ||
12088               element == EL_EM_EXIT_OPEN ||
12089               element == EL_SP_EXIT_OPEN ||
12090               element == EL_STEEL_EXIT_OPEN ||
12091               element == EL_EM_STEEL_EXIT_OPEN ||
12092               element == EL_SP_TERMINAL ||
12093               element == EL_SP_TERMINAL_ACTIVE ||
12094               element == EL_EXTRA_TIME ||
12095               element == EL_SHIELD_NORMAL ||
12096               element == EL_SHIELD_DEADLY) &&
12097              IS_ANIMATED(graphic))
12098       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12099     else if (IS_MOVING(x, y))
12100       ContinueMoving(x, y);
12101     else if (IS_ACTIVE_BOMB(element))
12102       CheckDynamite(x, y);
12103     else if (element == EL_AMOEBA_GROWING)
12104       AmoebaGrowing(x, y);
12105     else if (element == EL_AMOEBA_SHRINKING)
12106       AmoebaShrinking(x, y);
12107
12108 #if !USE_NEW_AMOEBA_CODE
12109     else if (IS_AMOEBALIVE(element))
12110       AmoebaReproduce(x, y);
12111 #endif
12112
12113     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12114       Life(x, y);
12115     else if (element == EL_EXIT_CLOSED)
12116       CheckExit(x, y);
12117     else if (element == EL_EM_EXIT_CLOSED)
12118       CheckExitEM(x, y);
12119     else if (element == EL_STEEL_EXIT_CLOSED)
12120       CheckExitSteel(x, y);
12121     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12122       CheckExitSteelEM(x, y);
12123     else if (element == EL_SP_EXIT_CLOSED)
12124       CheckExitSP(x, y);
12125     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12126              element == EL_EXPANDABLE_STEELWALL_GROWING)
12127       MauerWaechst(x, y);
12128     else if (element == EL_EXPANDABLE_WALL ||
12129              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12130              element == EL_EXPANDABLE_WALL_VERTICAL ||
12131              element == EL_EXPANDABLE_WALL_ANY ||
12132              element == EL_BD_EXPANDABLE_WALL)
12133       MauerAbleger(x, y);
12134     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12135              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12136              element == EL_EXPANDABLE_STEELWALL_ANY)
12137       MauerAblegerStahl(x, y);
12138     else if (element == EL_FLAMES)
12139       CheckForDragon(x, y);
12140     else if (element == EL_EXPLOSION)
12141       ; // drawing of correct explosion animation is handled separately
12142     else if (element == EL_ELEMENT_SNAPPING ||
12143              element == EL_DIAGONAL_SHRINKING ||
12144              element == EL_DIAGONAL_GROWING)
12145     {
12146       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12147
12148       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12149     }
12150     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12151       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12152
12153     if (IS_BELT_ACTIVE(element))
12154       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12155
12156     if (game.magic_wall_active)
12157     {
12158       int jx = local_player->jx, jy = local_player->jy;
12159
12160       // play the element sound at the position nearest to the player
12161       if ((element == EL_MAGIC_WALL_FULL ||
12162            element == EL_MAGIC_WALL_ACTIVE ||
12163            element == EL_MAGIC_WALL_EMPTYING ||
12164            element == EL_BD_MAGIC_WALL_FULL ||
12165            element == EL_BD_MAGIC_WALL_ACTIVE ||
12166            element == EL_BD_MAGIC_WALL_EMPTYING ||
12167            element == EL_DC_MAGIC_WALL_FULL ||
12168            element == EL_DC_MAGIC_WALL_ACTIVE ||
12169            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12170           ABS(x - jx) + ABS(y - jy) <
12171           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12172       {
12173         magic_wall_x = x;
12174         magic_wall_y = y;
12175       }
12176     }
12177   }
12178
12179 #if USE_NEW_AMOEBA_CODE
12180   // new experimental amoeba growth stuff
12181   if (!(FrameCounter % 8))
12182   {
12183     static unsigned int random = 1684108901;
12184
12185     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12186     {
12187       x = RND(lev_fieldx);
12188       y = RND(lev_fieldy);
12189       element = Tile[x][y];
12190
12191       if (!IS_PLAYER(x,y) &&
12192           (element == EL_EMPTY ||
12193            CAN_GROW_INTO(element) ||
12194            element == EL_QUICKSAND_EMPTY ||
12195            element == EL_QUICKSAND_FAST_EMPTY ||
12196            element == EL_ACID_SPLASH_LEFT ||
12197            element == EL_ACID_SPLASH_RIGHT))
12198       {
12199         if ((IN_LEV_FIELD(x, y-1) && Tile[x][y-1] == EL_AMOEBA_WET) ||
12200             (IN_LEV_FIELD(x-1, y) && Tile[x-1][y] == EL_AMOEBA_WET) ||
12201             (IN_LEV_FIELD(x+1, y) && Tile[x+1][y] == EL_AMOEBA_WET) ||
12202             (IN_LEV_FIELD(x, y+1) && Tile[x][y+1] == EL_AMOEBA_WET))
12203           Tile[x][y] = EL_AMOEBA_DROP;
12204       }
12205
12206       random = random * 129 + 1;
12207     }
12208   }
12209 #endif
12210
12211   game.explosions_delayed = FALSE;
12212
12213   SCAN_PLAYFIELD(x, y)
12214   {
12215     element = Tile[x][y];
12216
12217     if (ExplodeField[x][y])
12218       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12219     else if (element == EL_EXPLOSION)
12220       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12221
12222     ExplodeField[x][y] = EX_TYPE_NONE;
12223   }
12224
12225   game.explosions_delayed = TRUE;
12226
12227   if (game.magic_wall_active)
12228   {
12229     if (!(game.magic_wall_time_left % 4))
12230     {
12231       int element = Tile[magic_wall_x][magic_wall_y];
12232
12233       if (element == EL_BD_MAGIC_WALL_FULL ||
12234           element == EL_BD_MAGIC_WALL_ACTIVE ||
12235           element == EL_BD_MAGIC_WALL_EMPTYING)
12236         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12237       else if (element == EL_DC_MAGIC_WALL_FULL ||
12238                element == EL_DC_MAGIC_WALL_ACTIVE ||
12239                element == EL_DC_MAGIC_WALL_EMPTYING)
12240         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12241       else
12242         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12243     }
12244
12245     if (game.magic_wall_time_left > 0)
12246     {
12247       game.magic_wall_time_left--;
12248
12249       if (!game.magic_wall_time_left)
12250       {
12251         SCAN_PLAYFIELD(x, y)
12252         {
12253           element = Tile[x][y];
12254
12255           if (element == EL_MAGIC_WALL_ACTIVE ||
12256               element == EL_MAGIC_WALL_FULL)
12257           {
12258             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12259             TEST_DrawLevelField(x, y);
12260           }
12261           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12262                    element == EL_BD_MAGIC_WALL_FULL)
12263           {
12264             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12265             TEST_DrawLevelField(x, y);
12266           }
12267           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12268                    element == EL_DC_MAGIC_WALL_FULL)
12269           {
12270             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12271             TEST_DrawLevelField(x, y);
12272           }
12273         }
12274
12275         game.magic_wall_active = FALSE;
12276       }
12277     }
12278   }
12279
12280   if (game.light_time_left > 0)
12281   {
12282     game.light_time_left--;
12283
12284     if (game.light_time_left == 0)
12285       RedrawAllLightSwitchesAndInvisibleElements();
12286   }
12287
12288   if (game.timegate_time_left > 0)
12289   {
12290     game.timegate_time_left--;
12291
12292     if (game.timegate_time_left == 0)
12293       CloseAllOpenTimegates();
12294   }
12295
12296   if (game.lenses_time_left > 0)
12297   {
12298     game.lenses_time_left--;
12299
12300     if (game.lenses_time_left == 0)
12301       RedrawAllInvisibleElementsForLenses();
12302   }
12303
12304   if (game.magnify_time_left > 0)
12305   {
12306     game.magnify_time_left--;
12307
12308     if (game.magnify_time_left == 0)
12309       RedrawAllInvisibleElementsForMagnifier();
12310   }
12311
12312   for (i = 0; i < MAX_PLAYERS; i++)
12313   {
12314     struct PlayerInfo *player = &stored_player[i];
12315
12316     if (SHIELD_ON(player))
12317     {
12318       if (player->shield_deadly_time_left)
12319         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12320       else if (player->shield_normal_time_left)
12321         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12322     }
12323   }
12324
12325 #if USE_DELAYED_GFX_REDRAW
12326   SCAN_PLAYFIELD(x, y)
12327   {
12328     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12329     {
12330       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12331          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12332
12333       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12334         DrawLevelField(x, y);
12335
12336       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12337         DrawLevelFieldCrumbled(x, y);
12338
12339       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12340         DrawLevelFieldCrumbledNeighbours(x, y);
12341
12342       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12343         DrawTwinkleOnField(x, y);
12344     }
12345
12346     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12347   }
12348 #endif
12349
12350   DrawAllPlayers();
12351   PlayAllPlayersSound();
12352
12353   for (i = 0; i < MAX_PLAYERS; i++)
12354   {
12355     struct PlayerInfo *player = &stored_player[i];
12356
12357     if (player->show_envelope != 0 && (!player->active ||
12358                                        player->MovPos == 0))
12359     {
12360       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12361
12362       player->show_envelope = 0;
12363     }
12364   }
12365
12366   // use random number generator in every frame to make it less predictable
12367   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12368     RND(1);
12369
12370   mouse_action_last = mouse_action;
12371 }
12372
12373 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12374 {
12375   int min_x = x, min_y = y, max_x = x, max_y = y;
12376   int i;
12377
12378   for (i = 0; i < MAX_PLAYERS; i++)
12379   {
12380     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12381
12382     if (!stored_player[i].active || &stored_player[i] == player)
12383       continue;
12384
12385     min_x = MIN(min_x, jx);
12386     min_y = MIN(min_y, jy);
12387     max_x = MAX(max_x, jx);
12388     max_y = MAX(max_y, jy);
12389   }
12390
12391   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12392 }
12393
12394 static boolean AllPlayersInVisibleScreen(void)
12395 {
12396   int i;
12397
12398   for (i = 0; i < MAX_PLAYERS; i++)
12399   {
12400     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12401
12402     if (!stored_player[i].active)
12403       continue;
12404
12405     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12406       return FALSE;
12407   }
12408
12409   return TRUE;
12410 }
12411
12412 void ScrollLevel(int dx, int dy)
12413 {
12414   int scroll_offset = 2 * TILEX_VAR;
12415   int x, y;
12416
12417   BlitBitmap(drawto_field, drawto_field,
12418              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12419              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12420              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12421              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12422              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12423              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12424
12425   if (dx != 0)
12426   {
12427     x = (dx == 1 ? BX1 : BX2);
12428     for (y = BY1; y <= BY2; y++)
12429       DrawScreenField(x, y);
12430   }
12431
12432   if (dy != 0)
12433   {
12434     y = (dy == 1 ? BY1 : BY2);
12435     for (x = BX1; x <= BX2; x++)
12436       DrawScreenField(x, y);
12437   }
12438
12439   redraw_mask |= REDRAW_FIELD;
12440 }
12441
12442 static boolean canFallDown(struct PlayerInfo *player)
12443 {
12444   int jx = player->jx, jy = player->jy;
12445
12446   return (IN_LEV_FIELD(jx, jy + 1) &&
12447           (IS_FREE(jx, jy + 1) ||
12448            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12449           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12450           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12451 }
12452
12453 static boolean canPassField(int x, int y, int move_dir)
12454 {
12455   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12456   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12457   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12458   int nextx = x + dx;
12459   int nexty = y + dy;
12460   int element = Tile[x][y];
12461
12462   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12463           !CAN_MOVE(element) &&
12464           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12465           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12466           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12467 }
12468
12469 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12470 {
12471   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12472   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12473   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12474   int newx = x + dx;
12475   int newy = y + dy;
12476
12477   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12478           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12479           (IS_DIGGABLE(Tile[newx][newy]) ||
12480            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12481            canPassField(newx, newy, move_dir)));
12482 }
12483
12484 static void CheckGravityMovement(struct PlayerInfo *player)
12485 {
12486   if (player->gravity && !player->programmed_action)
12487   {
12488     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12489     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12490     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12491     int jx = player->jx, jy = player->jy;
12492     boolean player_is_moving_to_valid_field =
12493       (!player_is_snapping &&
12494        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12495         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12496     boolean player_can_fall_down = canFallDown(player);
12497
12498     if (player_can_fall_down &&
12499         !player_is_moving_to_valid_field)
12500       player->programmed_action = MV_DOWN;
12501   }
12502 }
12503
12504 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12505 {
12506   return CheckGravityMovement(player);
12507
12508   if (player->gravity && !player->programmed_action)
12509   {
12510     int jx = player->jx, jy = player->jy;
12511     boolean field_under_player_is_free =
12512       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12513     boolean player_is_standing_on_valid_field =
12514       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12515        (IS_WALKABLE(Tile[jx][jy]) &&
12516         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12517
12518     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12519       player->programmed_action = MV_DOWN;
12520   }
12521 }
12522
12523 /*
12524   MovePlayerOneStep()
12525   -----------------------------------------------------------------------------
12526   dx, dy:               direction (non-diagonal) to try to move the player to
12527   real_dx, real_dy:     direction as read from input device (can be diagonal)
12528 */
12529
12530 boolean MovePlayerOneStep(struct PlayerInfo *player,
12531                           int dx, int dy, int real_dx, int real_dy)
12532 {
12533   int jx = player->jx, jy = player->jy;
12534   int new_jx = jx + dx, new_jy = jy + dy;
12535   int can_move;
12536   boolean player_can_move = !player->cannot_move;
12537
12538   if (!player->active || (!dx && !dy))
12539     return MP_NO_ACTION;
12540
12541   player->MovDir = (dx < 0 ? MV_LEFT :
12542                     dx > 0 ? MV_RIGHT :
12543                     dy < 0 ? MV_UP :
12544                     dy > 0 ? MV_DOWN :  MV_NONE);
12545
12546   if (!IN_LEV_FIELD(new_jx, new_jy))
12547     return MP_NO_ACTION;
12548
12549   if (!player_can_move)
12550   {
12551     if (player->MovPos == 0)
12552     {
12553       player->is_moving = FALSE;
12554       player->is_digging = FALSE;
12555       player->is_collecting = FALSE;
12556       player->is_snapping = FALSE;
12557       player->is_pushing = FALSE;
12558     }
12559   }
12560
12561   if (!network.enabled && game.centered_player_nr == -1 &&
12562       !AllPlayersInSight(player, new_jx, new_jy))
12563     return MP_NO_ACTION;
12564
12565   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12566   if (can_move != MP_MOVING)
12567     return can_move;
12568
12569   // check if DigField() has caused relocation of the player
12570   if (player->jx != jx || player->jy != jy)
12571     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12572
12573   StorePlayer[jx][jy] = 0;
12574   player->last_jx = jx;
12575   player->last_jy = jy;
12576   player->jx = new_jx;
12577   player->jy = new_jy;
12578   StorePlayer[new_jx][new_jy] = player->element_nr;
12579
12580   if (player->move_delay_value_next != -1)
12581   {
12582     player->move_delay_value = player->move_delay_value_next;
12583     player->move_delay_value_next = -1;
12584   }
12585
12586   player->MovPos =
12587     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12588
12589   player->step_counter++;
12590
12591   PlayerVisit[jx][jy] = FrameCounter;
12592
12593   player->is_moving = TRUE;
12594
12595 #if 1
12596   // should better be called in MovePlayer(), but this breaks some tapes
12597   ScrollPlayer(player, SCROLL_INIT);
12598 #endif
12599
12600   return MP_MOVING;
12601 }
12602
12603 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12604 {
12605   int jx = player->jx, jy = player->jy;
12606   int old_jx = jx, old_jy = jy;
12607   int moved = MP_NO_ACTION;
12608
12609   if (!player->active)
12610     return FALSE;
12611
12612   if (!dx && !dy)
12613   {
12614     if (player->MovPos == 0)
12615     {
12616       player->is_moving = FALSE;
12617       player->is_digging = FALSE;
12618       player->is_collecting = FALSE;
12619       player->is_snapping = FALSE;
12620       player->is_pushing = FALSE;
12621     }
12622
12623     return FALSE;
12624   }
12625
12626   if (player->move_delay > 0)
12627     return FALSE;
12628
12629   player->move_delay = -1;              // set to "uninitialized" value
12630
12631   // store if player is automatically moved to next field
12632   player->is_auto_moving = (player->programmed_action != MV_NONE);
12633
12634   // remove the last programmed player action
12635   player->programmed_action = 0;
12636
12637   if (player->MovPos)
12638   {
12639     // should only happen if pre-1.2 tape recordings are played
12640     // this is only for backward compatibility
12641
12642     int original_move_delay_value = player->move_delay_value;
12643
12644 #if DEBUG
12645     Debug("game:playing:MovePlayer",
12646           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12647           tape.counter);
12648 #endif
12649
12650     // scroll remaining steps with finest movement resolution
12651     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12652
12653     while (player->MovPos)
12654     {
12655       ScrollPlayer(player, SCROLL_GO_ON);
12656       ScrollScreen(NULL, SCROLL_GO_ON);
12657
12658       AdvanceFrameAndPlayerCounters(player->index_nr);
12659
12660       DrawAllPlayers();
12661       BackToFront_WithFrameDelay(0);
12662     }
12663
12664     player->move_delay_value = original_move_delay_value;
12665   }
12666
12667   player->is_active = FALSE;
12668
12669   if (player->last_move_dir & MV_HORIZONTAL)
12670   {
12671     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12672       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12673   }
12674   else
12675   {
12676     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12677       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12678   }
12679
12680   if (!moved && !player->is_active)
12681   {
12682     player->is_moving = FALSE;
12683     player->is_digging = FALSE;
12684     player->is_collecting = FALSE;
12685     player->is_snapping = FALSE;
12686     player->is_pushing = FALSE;
12687   }
12688
12689   jx = player->jx;
12690   jy = player->jy;
12691
12692   if (moved & MP_MOVING && !ScreenMovPos &&
12693       (player->index_nr == game.centered_player_nr ||
12694        game.centered_player_nr == -1))
12695   {
12696     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12697
12698     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12699     {
12700       // actual player has left the screen -- scroll in that direction
12701       if (jx != old_jx)         // player has moved horizontally
12702         scroll_x += (jx - old_jx);
12703       else                      // player has moved vertically
12704         scroll_y += (jy - old_jy);
12705     }
12706     else
12707     {
12708       int offset_raw = game.scroll_delay_value;
12709
12710       if (jx != old_jx)         // player has moved horizontally
12711       {
12712         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12713         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12714         int new_scroll_x = jx - MIDPOSX + offset_x;
12715
12716         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12717             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12718           scroll_x = new_scroll_x;
12719
12720         // don't scroll over playfield boundaries
12721         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12722
12723         // don't scroll more than one field at a time
12724         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12725
12726         // don't scroll against the player's moving direction
12727         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12728             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12729           scroll_x = old_scroll_x;
12730       }
12731       else                      // player has moved vertically
12732       {
12733         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12734         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12735         int new_scroll_y = jy - MIDPOSY + offset_y;
12736
12737         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12738             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12739           scroll_y = new_scroll_y;
12740
12741         // don't scroll over playfield boundaries
12742         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
12743
12744         // don't scroll more than one field at a time
12745         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12746
12747         // don't scroll against the player's moving direction
12748         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12749             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12750           scroll_y = old_scroll_y;
12751       }
12752     }
12753
12754     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12755     {
12756       if (!network.enabled && game.centered_player_nr == -1 &&
12757           !AllPlayersInVisibleScreen())
12758       {
12759         scroll_x = old_scroll_x;
12760         scroll_y = old_scroll_y;
12761       }
12762       else
12763       {
12764         ScrollScreen(player, SCROLL_INIT);
12765         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12766       }
12767     }
12768   }
12769
12770   player->StepFrame = 0;
12771
12772   if (moved & MP_MOVING)
12773   {
12774     if (old_jx != jx && old_jy == jy)
12775       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12776     else if (old_jx == jx && old_jy != jy)
12777       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12778
12779     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12780
12781     player->last_move_dir = player->MovDir;
12782     player->is_moving = TRUE;
12783     player->is_snapping = FALSE;
12784     player->is_switching = FALSE;
12785     player->is_dropping = FALSE;
12786     player->is_dropping_pressed = FALSE;
12787     player->drop_pressed_delay = 0;
12788
12789 #if 0
12790     // should better be called here than above, but this breaks some tapes
12791     ScrollPlayer(player, SCROLL_INIT);
12792 #endif
12793   }
12794   else
12795   {
12796     CheckGravityMovementWhenNotMoving(player);
12797
12798     player->is_moving = FALSE;
12799
12800     /* at this point, the player is allowed to move, but cannot move right now
12801        (e.g. because of something blocking the way) -- ensure that the player
12802        is also allowed to move in the next frame (in old versions before 3.1.1,
12803        the player was forced to wait again for eight frames before next try) */
12804
12805     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12806       player->move_delay = 0;   // allow direct movement in the next frame
12807   }
12808
12809   if (player->move_delay == -1)         // not yet initialized by DigField()
12810     player->move_delay = player->move_delay_value;
12811
12812   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12813   {
12814     TestIfPlayerTouchesBadThing(jx, jy);
12815     TestIfPlayerTouchesCustomElement(jx, jy);
12816   }
12817
12818   if (!player->active)
12819     RemovePlayer(player);
12820
12821   return moved;
12822 }
12823
12824 void ScrollPlayer(struct PlayerInfo *player, int mode)
12825 {
12826   int jx = player->jx, jy = player->jy;
12827   int last_jx = player->last_jx, last_jy = player->last_jy;
12828   int move_stepsize = TILEX / player->move_delay_value;
12829
12830   if (!player->active)
12831     return;
12832
12833   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
12834     return;
12835
12836   if (mode == SCROLL_INIT)
12837   {
12838     player->actual_frame_counter = FrameCounter;
12839     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12840
12841     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12842         Tile[last_jx][last_jy] == EL_EMPTY)
12843     {
12844       int last_field_block_delay = 0;   // start with no blocking at all
12845       int block_delay_adjustment = player->block_delay_adjustment;
12846
12847       // if player blocks last field, add delay for exactly one move
12848       if (player->block_last_field)
12849       {
12850         last_field_block_delay += player->move_delay_value;
12851
12852         // when blocking enabled, prevent moving up despite gravity
12853         if (player->gravity && player->MovDir == MV_UP)
12854           block_delay_adjustment = -1;
12855       }
12856
12857       // add block delay adjustment (also possible when not blocking)
12858       last_field_block_delay += block_delay_adjustment;
12859
12860       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12861       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12862     }
12863
12864     if (player->MovPos != 0)    // player has not yet reached destination
12865       return;
12866   }
12867   else if (!FrameReached(&player->actual_frame_counter, 1))
12868     return;
12869
12870   if (player->MovPos != 0)
12871   {
12872     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12873     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12874
12875     // before DrawPlayer() to draw correct player graphic for this case
12876     if (player->MovPos == 0)
12877       CheckGravityMovement(player);
12878   }
12879
12880   if (player->MovPos == 0)      // player reached destination field
12881   {
12882     if (player->move_delay_reset_counter > 0)
12883     {
12884       player->move_delay_reset_counter--;
12885
12886       if (player->move_delay_reset_counter == 0)
12887       {
12888         // continue with normal speed after quickly moving through gate
12889         HALVE_PLAYER_SPEED(player);
12890
12891         // be able to make the next move without delay
12892         player->move_delay = 0;
12893       }
12894     }
12895
12896     player->last_jx = jx;
12897     player->last_jy = jy;
12898
12899     if (Tile[jx][jy] == EL_EXIT_OPEN ||
12900         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
12901         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
12902         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
12903         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12904         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12905         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
12906         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
12907     {
12908       ExitPlayer(player);
12909
12910       if (game.players_still_needed == 0 &&
12911           (game.friends_still_needed == 0 ||
12912            IS_SP_ELEMENT(Tile[jx][jy])))
12913         LevelSolved();
12914     }
12915
12916     // this breaks one level: "machine", level 000
12917     {
12918       int move_direction = player->MovDir;
12919       int enter_side = MV_DIR_OPPOSITE(move_direction);
12920       int leave_side = move_direction;
12921       int old_jx = last_jx;
12922       int old_jy = last_jy;
12923       int old_element = Tile[old_jx][old_jy];
12924       int new_element = Tile[jx][jy];
12925
12926       if (IS_CUSTOM_ELEMENT(old_element))
12927         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12928                                    CE_LEFT_BY_PLAYER,
12929                                    player->index_bit, leave_side);
12930
12931       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12932                                           CE_PLAYER_LEAVES_X,
12933                                           player->index_bit, leave_side);
12934
12935       if (IS_CUSTOM_ELEMENT(new_element))
12936         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12937                                    player->index_bit, enter_side);
12938
12939       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12940                                           CE_PLAYER_ENTERS_X,
12941                                           player->index_bit, enter_side);
12942
12943       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12944                                         CE_MOVE_OF_X, move_direction);
12945     }
12946
12947     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12948     {
12949       TestIfPlayerTouchesBadThing(jx, jy);
12950       TestIfPlayerTouchesCustomElement(jx, jy);
12951
12952       /* needed because pushed element has not yet reached its destination,
12953          so it would trigger a change event at its previous field location */
12954       if (!player->is_pushing)
12955         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
12956
12957       if (!player->active)
12958         RemovePlayer(player);
12959     }
12960
12961     if (!game.LevelSolved && level.use_step_counter)
12962     {
12963       int i;
12964
12965       TimePlayed++;
12966
12967       if (TimeLeft > 0)
12968       {
12969         TimeLeft--;
12970
12971         if (TimeLeft <= 10 && setup.time_limit)
12972           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12973
12974         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12975
12976         DisplayGameControlValues();
12977
12978         if (!TimeLeft && setup.time_limit)
12979           for (i = 0; i < MAX_PLAYERS; i++)
12980             KillPlayer(&stored_player[i]);
12981       }
12982       else if (game.no_time_limit && !game.all_players_gone)
12983       {
12984         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12985
12986         DisplayGameControlValues();
12987       }
12988     }
12989
12990     if (tape.single_step && tape.recording && !tape.pausing &&
12991         !player->programmed_action)
12992       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12993
12994     if (!player->programmed_action)
12995       CheckSaveEngineSnapshot(player);
12996   }
12997 }
12998
12999 void ScrollScreen(struct PlayerInfo *player, int mode)
13000 {
13001   static unsigned int screen_frame_counter = 0;
13002
13003   if (mode == SCROLL_INIT)
13004   {
13005     // set scrolling step size according to actual player's moving speed
13006     ScrollStepSize = TILEX / player->move_delay_value;
13007
13008     screen_frame_counter = FrameCounter;
13009     ScreenMovDir = player->MovDir;
13010     ScreenMovPos = player->MovPos;
13011     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13012     return;
13013   }
13014   else if (!FrameReached(&screen_frame_counter, 1))
13015     return;
13016
13017   if (ScreenMovPos)
13018   {
13019     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13020     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13021     redraw_mask |= REDRAW_FIELD;
13022   }
13023   else
13024     ScreenMovDir = MV_NONE;
13025 }
13026
13027 void TestIfPlayerTouchesCustomElement(int x, int y)
13028 {
13029   static int xy[4][2] =
13030   {
13031     { 0, -1 },
13032     { -1, 0 },
13033     { +1, 0 },
13034     { 0, +1 }
13035   };
13036   static int trigger_sides[4][2] =
13037   {
13038     // center side       border side
13039     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13040     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13041     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13042     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13043   };
13044   static int touch_dir[4] =
13045   {
13046     MV_LEFT | MV_RIGHT,
13047     MV_UP   | MV_DOWN,
13048     MV_UP   | MV_DOWN,
13049     MV_LEFT | MV_RIGHT
13050   };
13051   int center_element = Tile[x][y];      // should always be non-moving!
13052   int i;
13053
13054   for (i = 0; i < NUM_DIRECTIONS; i++)
13055   {
13056     int xx = x + xy[i][0];
13057     int yy = y + xy[i][1];
13058     int center_side = trigger_sides[i][0];
13059     int border_side = trigger_sides[i][1];
13060     int border_element;
13061
13062     if (!IN_LEV_FIELD(xx, yy))
13063       continue;
13064
13065     if (IS_PLAYER(x, y))                // player found at center element
13066     {
13067       struct PlayerInfo *player = PLAYERINFO(x, y);
13068
13069       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13070         border_element = Tile[xx][yy];          // may be moving!
13071       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13072         border_element = Tile[xx][yy];
13073       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13074         border_element = MovingOrBlocked2Element(xx, yy);
13075       else
13076         continue;               // center and border element do not touch
13077
13078       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13079                                  player->index_bit, border_side);
13080       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13081                                           CE_PLAYER_TOUCHES_X,
13082                                           player->index_bit, border_side);
13083
13084       {
13085         /* use player element that is initially defined in the level playfield,
13086            not the player element that corresponds to the runtime player number
13087            (example: a level that contains EL_PLAYER_3 as the only player would
13088            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13089         int player_element = PLAYERINFO(x, y)->initial_element;
13090
13091         CheckElementChangeBySide(xx, yy, border_element, player_element,
13092                                  CE_TOUCHING_X, border_side);
13093       }
13094     }
13095     else if (IS_PLAYER(xx, yy))         // player found at border element
13096     {
13097       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13098
13099       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13100       {
13101         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13102           continue;             // center and border element do not touch
13103       }
13104
13105       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13106                                  player->index_bit, center_side);
13107       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13108                                           CE_PLAYER_TOUCHES_X,
13109                                           player->index_bit, center_side);
13110
13111       {
13112         /* use player element that is initially defined in the level playfield,
13113            not the player element that corresponds to the runtime player number
13114            (example: a level that contains EL_PLAYER_3 as the only player would
13115            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13116         int player_element = PLAYERINFO(xx, yy)->initial_element;
13117
13118         CheckElementChangeBySide(x, y, center_element, player_element,
13119                                  CE_TOUCHING_X, center_side);
13120       }
13121
13122       break;
13123     }
13124   }
13125 }
13126
13127 void TestIfElementTouchesCustomElement(int x, int y)
13128 {
13129   static int xy[4][2] =
13130   {
13131     { 0, -1 },
13132     { -1, 0 },
13133     { +1, 0 },
13134     { 0, +1 }
13135   };
13136   static int trigger_sides[4][2] =
13137   {
13138     // center side      border side
13139     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13140     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13141     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13142     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13143   };
13144   static int touch_dir[4] =
13145   {
13146     MV_LEFT | MV_RIGHT,
13147     MV_UP   | MV_DOWN,
13148     MV_UP   | MV_DOWN,
13149     MV_LEFT | MV_RIGHT
13150   };
13151   boolean change_center_element = FALSE;
13152   int center_element = Tile[x][y];      // should always be non-moving!
13153   int border_element_old[NUM_DIRECTIONS];
13154   int i;
13155
13156   for (i = 0; i < NUM_DIRECTIONS; i++)
13157   {
13158     int xx = x + xy[i][0];
13159     int yy = y + xy[i][1];
13160     int border_element;
13161
13162     border_element_old[i] = -1;
13163
13164     if (!IN_LEV_FIELD(xx, yy))
13165       continue;
13166
13167     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13168       border_element = Tile[xx][yy];    // may be moving!
13169     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13170       border_element = Tile[xx][yy];
13171     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13172       border_element = MovingOrBlocked2Element(xx, yy);
13173     else
13174       continue;                 // center and border element do not touch
13175
13176     border_element_old[i] = border_element;
13177   }
13178
13179   for (i = 0; i < NUM_DIRECTIONS; i++)
13180   {
13181     int xx = x + xy[i][0];
13182     int yy = y + xy[i][1];
13183     int center_side = trigger_sides[i][0];
13184     int border_element = border_element_old[i];
13185
13186     if (border_element == -1)
13187       continue;
13188
13189     // check for change of border element
13190     CheckElementChangeBySide(xx, yy, border_element, center_element,
13191                              CE_TOUCHING_X, center_side);
13192
13193     // (center element cannot be player, so we dont have to check this here)
13194   }
13195
13196   for (i = 0; i < NUM_DIRECTIONS; i++)
13197   {
13198     int xx = x + xy[i][0];
13199     int yy = y + xy[i][1];
13200     int border_side = trigger_sides[i][1];
13201     int border_element = border_element_old[i];
13202
13203     if (border_element == -1)
13204       continue;
13205
13206     // check for change of center element (but change it only once)
13207     if (!change_center_element)
13208       change_center_element =
13209         CheckElementChangeBySide(x, y, center_element, border_element,
13210                                  CE_TOUCHING_X, border_side);
13211
13212     if (IS_PLAYER(xx, yy))
13213     {
13214       /* use player element that is initially defined in the level playfield,
13215          not the player element that corresponds to the runtime player number
13216          (example: a level that contains EL_PLAYER_3 as the only player would
13217          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13218       int player_element = PLAYERINFO(xx, yy)->initial_element;
13219
13220       CheckElementChangeBySide(x, y, center_element, player_element,
13221                                CE_TOUCHING_X, border_side);
13222     }
13223   }
13224 }
13225
13226 void TestIfElementHitsCustomElement(int x, int y, int direction)
13227 {
13228   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13229   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13230   int hitx = x + dx, hity = y + dy;
13231   int hitting_element = Tile[x][y];
13232   int touched_element;
13233
13234   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13235     return;
13236
13237   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13238                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13239
13240   if (IN_LEV_FIELD(hitx, hity))
13241   {
13242     int opposite_direction = MV_DIR_OPPOSITE(direction);
13243     int hitting_side = direction;
13244     int touched_side = opposite_direction;
13245     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13246                           MovDir[hitx][hity] != direction ||
13247                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13248
13249     object_hit = TRUE;
13250
13251     if (object_hit)
13252     {
13253       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13254                                CE_HITTING_X, touched_side);
13255
13256       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13257                                CE_HIT_BY_X, hitting_side);
13258
13259       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13260                                CE_HIT_BY_SOMETHING, opposite_direction);
13261
13262       if (IS_PLAYER(hitx, hity))
13263       {
13264         /* use player element that is initially defined in the level playfield,
13265            not the player element that corresponds to the runtime player number
13266            (example: a level that contains EL_PLAYER_3 as the only player would
13267            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13268         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13269
13270         CheckElementChangeBySide(x, y, hitting_element, player_element,
13271                                  CE_HITTING_X, touched_side);
13272       }
13273     }
13274   }
13275
13276   // "hitting something" is also true when hitting the playfield border
13277   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13278                            CE_HITTING_SOMETHING, direction);
13279 }
13280
13281 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13282 {
13283   int i, kill_x = -1, kill_y = -1;
13284
13285   int bad_element = -1;
13286   static int test_xy[4][2] =
13287   {
13288     { 0, -1 },
13289     { -1, 0 },
13290     { +1, 0 },
13291     { 0, +1 }
13292   };
13293   static int test_dir[4] =
13294   {
13295     MV_UP,
13296     MV_LEFT,
13297     MV_RIGHT,
13298     MV_DOWN
13299   };
13300
13301   for (i = 0; i < NUM_DIRECTIONS; i++)
13302   {
13303     int test_x, test_y, test_move_dir, test_element;
13304
13305     test_x = good_x + test_xy[i][0];
13306     test_y = good_y + test_xy[i][1];
13307
13308     if (!IN_LEV_FIELD(test_x, test_y))
13309       continue;
13310
13311     test_move_dir =
13312       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13313
13314     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13315
13316     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13317        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13318     */
13319     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13320         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13321     {
13322       kill_x = test_x;
13323       kill_y = test_y;
13324       bad_element = test_element;
13325
13326       break;
13327     }
13328   }
13329
13330   if (kill_x != -1 || kill_y != -1)
13331   {
13332     if (IS_PLAYER(good_x, good_y))
13333     {
13334       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13335
13336       if (player->shield_deadly_time_left > 0 &&
13337           !IS_INDESTRUCTIBLE(bad_element))
13338         Bang(kill_x, kill_y);
13339       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13340         KillPlayer(player);
13341     }
13342     else
13343       Bang(good_x, good_y);
13344   }
13345 }
13346
13347 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13348 {
13349   int i, kill_x = -1, kill_y = -1;
13350   int bad_element = Tile[bad_x][bad_y];
13351   static int test_xy[4][2] =
13352   {
13353     { 0, -1 },
13354     { -1, 0 },
13355     { +1, 0 },
13356     { 0, +1 }
13357   };
13358   static int touch_dir[4] =
13359   {
13360     MV_LEFT | MV_RIGHT,
13361     MV_UP   | MV_DOWN,
13362     MV_UP   | MV_DOWN,
13363     MV_LEFT | MV_RIGHT
13364   };
13365   static int test_dir[4] =
13366   {
13367     MV_UP,
13368     MV_LEFT,
13369     MV_RIGHT,
13370     MV_DOWN
13371   };
13372
13373   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13374     return;
13375
13376   for (i = 0; i < NUM_DIRECTIONS; i++)
13377   {
13378     int test_x, test_y, test_move_dir, test_element;
13379
13380     test_x = bad_x + test_xy[i][0];
13381     test_y = bad_y + test_xy[i][1];
13382
13383     if (!IN_LEV_FIELD(test_x, test_y))
13384       continue;
13385
13386     test_move_dir =
13387       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13388
13389     test_element = Tile[test_x][test_y];
13390
13391     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13392        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13393     */
13394     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13395         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13396     {
13397       // good thing is player or penguin that does not move away
13398       if (IS_PLAYER(test_x, test_y))
13399       {
13400         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13401
13402         if (bad_element == EL_ROBOT && player->is_moving)
13403           continue;     // robot does not kill player if he is moving
13404
13405         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13406         {
13407           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13408             continue;           // center and border element do not touch
13409         }
13410
13411         kill_x = test_x;
13412         kill_y = test_y;
13413
13414         break;
13415       }
13416       else if (test_element == EL_PENGUIN)
13417       {
13418         kill_x = test_x;
13419         kill_y = test_y;
13420
13421         break;
13422       }
13423     }
13424   }
13425
13426   if (kill_x != -1 || kill_y != -1)
13427   {
13428     if (IS_PLAYER(kill_x, kill_y))
13429     {
13430       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13431
13432       if (player->shield_deadly_time_left > 0 &&
13433           !IS_INDESTRUCTIBLE(bad_element))
13434         Bang(bad_x, bad_y);
13435       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13436         KillPlayer(player);
13437     }
13438     else
13439       Bang(kill_x, kill_y);
13440   }
13441 }
13442
13443 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13444 {
13445   int bad_element = Tile[bad_x][bad_y];
13446   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13447   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13448   int test_x = bad_x + dx, test_y = bad_y + dy;
13449   int test_move_dir, test_element;
13450   int kill_x = -1, kill_y = -1;
13451
13452   if (!IN_LEV_FIELD(test_x, test_y))
13453     return;
13454
13455   test_move_dir =
13456     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13457
13458   test_element = Tile[test_x][test_y];
13459
13460   if (test_move_dir != bad_move_dir)
13461   {
13462     // good thing can be player or penguin that does not move away
13463     if (IS_PLAYER(test_x, test_y))
13464     {
13465       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13466
13467       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13468          player as being hit when he is moving towards the bad thing, because
13469          the "get hit by" condition would be lost after the player stops) */
13470       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13471         return;         // player moves away from bad thing
13472
13473       kill_x = test_x;
13474       kill_y = test_y;
13475     }
13476     else if (test_element == EL_PENGUIN)
13477     {
13478       kill_x = test_x;
13479       kill_y = test_y;
13480     }
13481   }
13482
13483   if (kill_x != -1 || kill_y != -1)
13484   {
13485     if (IS_PLAYER(kill_x, kill_y))
13486     {
13487       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13488
13489       if (player->shield_deadly_time_left > 0 &&
13490           !IS_INDESTRUCTIBLE(bad_element))
13491         Bang(bad_x, bad_y);
13492       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13493         KillPlayer(player);
13494     }
13495     else
13496       Bang(kill_x, kill_y);
13497   }
13498 }
13499
13500 void TestIfPlayerTouchesBadThing(int x, int y)
13501 {
13502   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13503 }
13504
13505 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13506 {
13507   TestIfGoodThingHitsBadThing(x, y, move_dir);
13508 }
13509
13510 void TestIfBadThingTouchesPlayer(int x, int y)
13511 {
13512   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13513 }
13514
13515 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13516 {
13517   TestIfBadThingHitsGoodThing(x, y, move_dir);
13518 }
13519
13520 void TestIfFriendTouchesBadThing(int x, int y)
13521 {
13522   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13523 }
13524
13525 void TestIfBadThingTouchesFriend(int x, int y)
13526 {
13527   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13528 }
13529
13530 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13531 {
13532   int i, kill_x = bad_x, kill_y = bad_y;
13533   static int xy[4][2] =
13534   {
13535     { 0, -1 },
13536     { -1, 0 },
13537     { +1, 0 },
13538     { 0, +1 }
13539   };
13540
13541   for (i = 0; i < NUM_DIRECTIONS; i++)
13542   {
13543     int x, y, element;
13544
13545     x = bad_x + xy[i][0];
13546     y = bad_y + xy[i][1];
13547     if (!IN_LEV_FIELD(x, y))
13548       continue;
13549
13550     element = Tile[x][y];
13551     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13552         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13553     {
13554       kill_x = x;
13555       kill_y = y;
13556       break;
13557     }
13558   }
13559
13560   if (kill_x != bad_x || kill_y != bad_y)
13561     Bang(bad_x, bad_y);
13562 }
13563
13564 void KillPlayer(struct PlayerInfo *player)
13565 {
13566   int jx = player->jx, jy = player->jy;
13567
13568   if (!player->active)
13569     return;
13570
13571 #if 0
13572   Debug("game:playing:KillPlayer",
13573         "0: killed == %d, active == %d, reanimated == %d",
13574         player->killed, player->active, player->reanimated);
13575 #endif
13576
13577   /* the following code was introduced to prevent an infinite loop when calling
13578      -> Bang()
13579      -> CheckTriggeredElementChangeExt()
13580      -> ExecuteCustomElementAction()
13581      -> KillPlayer()
13582      -> (infinitely repeating the above sequence of function calls)
13583      which occurs when killing the player while having a CE with the setting
13584      "kill player X when explosion of <player X>"; the solution using a new
13585      field "player->killed" was chosen for backwards compatibility, although
13586      clever use of the fields "player->active" etc. would probably also work */
13587 #if 1
13588   if (player->killed)
13589     return;
13590 #endif
13591
13592   player->killed = TRUE;
13593
13594   // remove accessible field at the player's position
13595   Tile[jx][jy] = EL_EMPTY;
13596
13597   // deactivate shield (else Bang()/Explode() would not work right)
13598   player->shield_normal_time_left = 0;
13599   player->shield_deadly_time_left = 0;
13600
13601 #if 0
13602   Debug("game:playing:KillPlayer",
13603         "1: killed == %d, active == %d, reanimated == %d",
13604         player->killed, player->active, player->reanimated);
13605 #endif
13606
13607   Bang(jx, jy);
13608
13609 #if 0
13610   Debug("game:playing:KillPlayer",
13611         "2: killed == %d, active == %d, reanimated == %d",
13612         player->killed, player->active, player->reanimated);
13613 #endif
13614
13615   if (player->reanimated)       // killed player may have been reanimated
13616     player->killed = player->reanimated = FALSE;
13617   else
13618     BuryPlayer(player);
13619 }
13620
13621 static void KillPlayerUnlessEnemyProtected(int x, int y)
13622 {
13623   if (!PLAYER_ENEMY_PROTECTED(x, y))
13624     KillPlayer(PLAYERINFO(x, y));
13625 }
13626
13627 static void KillPlayerUnlessExplosionProtected(int x, int y)
13628 {
13629   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13630     KillPlayer(PLAYERINFO(x, y));
13631 }
13632
13633 void BuryPlayer(struct PlayerInfo *player)
13634 {
13635   int jx = player->jx, jy = player->jy;
13636
13637   if (!player->active)
13638     return;
13639
13640   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13641   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13642
13643   RemovePlayer(player);
13644
13645   player->buried = TRUE;
13646
13647   if (game.all_players_gone)
13648     game.GameOver = TRUE;
13649 }
13650
13651 void RemovePlayer(struct PlayerInfo *player)
13652 {
13653   int jx = player->jx, jy = player->jy;
13654   int i, found = FALSE;
13655
13656   player->present = FALSE;
13657   player->active = FALSE;
13658
13659   // required for some CE actions (even if the player is not active anymore)
13660   player->MovPos = 0;
13661
13662   if (!ExplodeField[jx][jy])
13663     StorePlayer[jx][jy] = 0;
13664
13665   if (player->is_moving)
13666     TEST_DrawLevelField(player->last_jx, player->last_jy);
13667
13668   for (i = 0; i < MAX_PLAYERS; i++)
13669     if (stored_player[i].active)
13670       found = TRUE;
13671
13672   if (!found)
13673   {
13674     game.all_players_gone = TRUE;
13675     game.GameOver = TRUE;
13676   }
13677
13678   game.exit_x = game.robot_wheel_x = jx;
13679   game.exit_y = game.robot_wheel_y = jy;
13680 }
13681
13682 void ExitPlayer(struct PlayerInfo *player)
13683 {
13684   DrawPlayer(player);   // needed here only to cleanup last field
13685   RemovePlayer(player);
13686
13687   if (game.players_still_needed > 0)
13688     game.players_still_needed--;
13689 }
13690
13691 static void setFieldForSnapping(int x, int y, int element, int direction)
13692 {
13693   struct ElementInfo *ei = &element_info[element];
13694   int direction_bit = MV_DIR_TO_BIT(direction);
13695   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13696   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13697                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13698
13699   Tile[x][y] = EL_ELEMENT_SNAPPING;
13700   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13701
13702   ResetGfxAnimation(x, y);
13703
13704   GfxElement[x][y] = element;
13705   GfxAction[x][y] = action;
13706   GfxDir[x][y] = direction;
13707   GfxFrame[x][y] = -1;
13708 }
13709
13710 /*
13711   =============================================================================
13712   checkDiagonalPushing()
13713   -----------------------------------------------------------------------------
13714   check if diagonal input device direction results in pushing of object
13715   (by checking if the alternative direction is walkable, diggable, ...)
13716   =============================================================================
13717 */
13718
13719 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13720                                     int x, int y, int real_dx, int real_dy)
13721 {
13722   int jx, jy, dx, dy, xx, yy;
13723
13724   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13725     return TRUE;
13726
13727   // diagonal direction: check alternative direction
13728   jx = player->jx;
13729   jy = player->jy;
13730   dx = x - jx;
13731   dy = y - jy;
13732   xx = jx + (dx == 0 ? real_dx : 0);
13733   yy = jy + (dy == 0 ? real_dy : 0);
13734
13735   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
13736 }
13737
13738 /*
13739   =============================================================================
13740   DigField()
13741   -----------------------------------------------------------------------------
13742   x, y:                 field next to player (non-diagonal) to try to dig to
13743   real_dx, real_dy:     direction as read from input device (can be diagonal)
13744   =============================================================================
13745 */
13746
13747 static int DigField(struct PlayerInfo *player,
13748                     int oldx, int oldy, int x, int y,
13749                     int real_dx, int real_dy, int mode)
13750 {
13751   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13752   boolean player_was_pushing = player->is_pushing;
13753   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13754   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13755   int jx = oldx, jy = oldy;
13756   int dx = x - jx, dy = y - jy;
13757   int nextx = x + dx, nexty = y + dy;
13758   int move_direction = (dx == -1 ? MV_LEFT  :
13759                         dx == +1 ? MV_RIGHT :
13760                         dy == -1 ? MV_UP    :
13761                         dy == +1 ? MV_DOWN  : MV_NONE);
13762   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13763   int dig_side = MV_DIR_OPPOSITE(move_direction);
13764   int old_element = Tile[jx][jy];
13765   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13766   int collect_count;
13767
13768   if (is_player)                // function can also be called by EL_PENGUIN
13769   {
13770     if (player->MovPos == 0)
13771     {
13772       player->is_digging = FALSE;
13773       player->is_collecting = FALSE;
13774     }
13775
13776     if (player->MovPos == 0)    // last pushing move finished
13777       player->is_pushing = FALSE;
13778
13779     if (mode == DF_NO_PUSH)     // player just stopped pushing
13780     {
13781       player->is_switching = FALSE;
13782       player->push_delay = -1;
13783
13784       return MP_NO_ACTION;
13785     }
13786   }
13787
13788   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13789     old_element = Back[jx][jy];
13790
13791   // in case of element dropped at player position, check background
13792   else if (Back[jx][jy] != EL_EMPTY &&
13793            game.engine_version >= VERSION_IDENT(2,2,0,0))
13794     old_element = Back[jx][jy];
13795
13796   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13797     return MP_NO_ACTION;        // field has no opening in this direction
13798
13799   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13800     return MP_NO_ACTION;        // field has no opening in this direction
13801
13802   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13803   {
13804     SplashAcid(x, y);
13805
13806     Tile[jx][jy] = player->artwork_element;
13807     InitMovingField(jx, jy, MV_DOWN);
13808     Store[jx][jy] = EL_ACID;
13809     ContinueMoving(jx, jy);
13810     BuryPlayer(player);
13811
13812     return MP_DONT_RUN_INTO;
13813   }
13814
13815   if (player_can_move && DONT_RUN_INTO(element))
13816   {
13817     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13818
13819     return MP_DONT_RUN_INTO;
13820   }
13821
13822   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13823     return MP_NO_ACTION;
13824
13825   collect_count = element_info[element].collect_count_initial;
13826
13827   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
13828     return MP_NO_ACTION;
13829
13830   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13831     player_can_move = player_can_move_or_snap;
13832
13833   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13834       game.engine_version >= VERSION_IDENT(2,2,0,0))
13835   {
13836     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13837                                player->index_bit, dig_side);
13838     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13839                                         player->index_bit, dig_side);
13840
13841     if (element == EL_DC_LANDMINE)
13842       Bang(x, y);
13843
13844     if (Tile[x][y] != element)          // field changed by snapping
13845       return MP_ACTION;
13846
13847     return MP_NO_ACTION;
13848   }
13849
13850   if (player->gravity && is_player && !player->is_auto_moving &&
13851       canFallDown(player) && move_direction != MV_DOWN &&
13852       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13853     return MP_NO_ACTION;        // player cannot walk here due to gravity
13854
13855   if (player_can_move &&
13856       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13857   {
13858     int sound_element = SND_ELEMENT(element);
13859     int sound_action = ACTION_WALKING;
13860
13861     if (IS_RND_GATE(element))
13862     {
13863       if (!player->key[RND_GATE_NR(element)])
13864         return MP_NO_ACTION;
13865     }
13866     else if (IS_RND_GATE_GRAY(element))
13867     {
13868       if (!player->key[RND_GATE_GRAY_NR(element)])
13869         return MP_NO_ACTION;
13870     }
13871     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13872     {
13873       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13874         return MP_NO_ACTION;
13875     }
13876     else if (element == EL_EXIT_OPEN ||
13877              element == EL_EM_EXIT_OPEN ||
13878              element == EL_EM_EXIT_OPENING ||
13879              element == EL_STEEL_EXIT_OPEN ||
13880              element == EL_EM_STEEL_EXIT_OPEN ||
13881              element == EL_EM_STEEL_EXIT_OPENING ||
13882              element == EL_SP_EXIT_OPEN ||
13883              element == EL_SP_EXIT_OPENING)
13884     {
13885       sound_action = ACTION_PASSING;    // player is passing exit
13886     }
13887     else if (element == EL_EMPTY)
13888     {
13889       sound_action = ACTION_MOVING;             // nothing to walk on
13890     }
13891
13892     // play sound from background or player, whatever is available
13893     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13894       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13895     else
13896       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13897   }
13898   else if (player_can_move &&
13899            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13900   {
13901     if (!ACCESS_FROM(element, opposite_direction))
13902       return MP_NO_ACTION;      // field not accessible from this direction
13903
13904     if (CAN_MOVE(element))      // only fixed elements can be passed!
13905       return MP_NO_ACTION;
13906
13907     if (IS_EM_GATE(element))
13908     {
13909       if (!player->key[EM_GATE_NR(element)])
13910         return MP_NO_ACTION;
13911     }
13912     else if (IS_EM_GATE_GRAY(element))
13913     {
13914       if (!player->key[EM_GATE_GRAY_NR(element)])
13915         return MP_NO_ACTION;
13916     }
13917     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13918     {
13919       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13920         return MP_NO_ACTION;
13921     }
13922     else if (IS_EMC_GATE(element))
13923     {
13924       if (!player->key[EMC_GATE_NR(element)])
13925         return MP_NO_ACTION;
13926     }
13927     else if (IS_EMC_GATE_GRAY(element))
13928     {
13929       if (!player->key[EMC_GATE_GRAY_NR(element)])
13930         return MP_NO_ACTION;
13931     }
13932     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13933     {
13934       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13935         return MP_NO_ACTION;
13936     }
13937     else if (element == EL_DC_GATE_WHITE ||
13938              element == EL_DC_GATE_WHITE_GRAY ||
13939              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13940     {
13941       if (player->num_white_keys == 0)
13942         return MP_NO_ACTION;
13943
13944       player->num_white_keys--;
13945     }
13946     else if (IS_SP_PORT(element))
13947     {
13948       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13949           element == EL_SP_GRAVITY_PORT_RIGHT ||
13950           element == EL_SP_GRAVITY_PORT_UP ||
13951           element == EL_SP_GRAVITY_PORT_DOWN)
13952         player->gravity = !player->gravity;
13953       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13954                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13955                element == EL_SP_GRAVITY_ON_PORT_UP ||
13956                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13957         player->gravity = TRUE;
13958       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13959                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13960                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13961                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13962         player->gravity = FALSE;
13963     }
13964
13965     // automatically move to the next field with double speed
13966     player->programmed_action = move_direction;
13967
13968     if (player->move_delay_reset_counter == 0)
13969     {
13970       player->move_delay_reset_counter = 2;     // two double speed steps
13971
13972       DOUBLE_PLAYER_SPEED(player);
13973     }
13974
13975     PlayLevelSoundAction(x, y, ACTION_PASSING);
13976   }
13977   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13978   {
13979     RemoveField(x, y);
13980
13981     if (mode != DF_SNAP)
13982     {
13983       GfxElement[x][y] = GFX_ELEMENT(element);
13984       player->is_digging = TRUE;
13985     }
13986
13987     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13988
13989     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13990                                         player->index_bit, dig_side);
13991
13992     if (mode == DF_SNAP)
13993     {
13994       if (level.block_snap_field)
13995         setFieldForSnapping(x, y, element, move_direction);
13996       else
13997         TestIfElementTouchesCustomElement(x, y);        // for empty space
13998
13999       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14000                                           player->index_bit, dig_side);
14001     }
14002   }
14003   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14004   {
14005     RemoveField(x, y);
14006
14007     if (is_player && mode != DF_SNAP)
14008     {
14009       GfxElement[x][y] = element;
14010       player->is_collecting = TRUE;
14011     }
14012
14013     if (element == EL_SPEED_PILL)
14014     {
14015       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14016     }
14017     else if (element == EL_EXTRA_TIME && level.time > 0)
14018     {
14019       TimeLeft += level.extra_time;
14020
14021       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14022
14023       DisplayGameControlValues();
14024     }
14025     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14026     {
14027       player->shield_normal_time_left += level.shield_normal_time;
14028       if (element == EL_SHIELD_DEADLY)
14029         player->shield_deadly_time_left += level.shield_deadly_time;
14030     }
14031     else if (element == EL_DYNAMITE ||
14032              element == EL_EM_DYNAMITE ||
14033              element == EL_SP_DISK_RED)
14034     {
14035       if (player->inventory_size < MAX_INVENTORY_SIZE)
14036         player->inventory_element[player->inventory_size++] = element;
14037
14038       DrawGameDoorValues();
14039     }
14040     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14041     {
14042       player->dynabomb_count++;
14043       player->dynabombs_left++;
14044     }
14045     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14046     {
14047       player->dynabomb_size++;
14048     }
14049     else if (element == EL_DYNABOMB_INCREASE_POWER)
14050     {
14051       player->dynabomb_xl = TRUE;
14052     }
14053     else if (IS_KEY(element))
14054     {
14055       player->key[KEY_NR(element)] = TRUE;
14056
14057       DrawGameDoorValues();
14058     }
14059     else if (element == EL_DC_KEY_WHITE)
14060     {
14061       player->num_white_keys++;
14062
14063       // display white keys?
14064       // DrawGameDoorValues();
14065     }
14066     else if (IS_ENVELOPE(element))
14067     {
14068       player->show_envelope = element;
14069     }
14070     else if (element == EL_EMC_LENSES)
14071     {
14072       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14073
14074       RedrawAllInvisibleElementsForLenses();
14075     }
14076     else if (element == EL_EMC_MAGNIFIER)
14077     {
14078       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14079
14080       RedrawAllInvisibleElementsForMagnifier();
14081     }
14082     else if (IS_DROPPABLE(element) ||
14083              IS_THROWABLE(element))     // can be collected and dropped
14084     {
14085       int i;
14086
14087       if (collect_count == 0)
14088         player->inventory_infinite_element = element;
14089       else
14090         for (i = 0; i < collect_count; i++)
14091           if (player->inventory_size < MAX_INVENTORY_SIZE)
14092             player->inventory_element[player->inventory_size++] = element;
14093
14094       DrawGameDoorValues();
14095     }
14096     else if (collect_count > 0)
14097     {
14098       game.gems_still_needed -= collect_count;
14099       if (game.gems_still_needed < 0)
14100         game.gems_still_needed = 0;
14101
14102       game.snapshot.collected_item = TRUE;
14103
14104       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14105
14106       DisplayGameControlValues();
14107     }
14108
14109     RaiseScoreElement(element);
14110     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14111
14112     if (is_player)
14113       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14114                                           player->index_bit, dig_side);
14115
14116     if (mode == DF_SNAP)
14117     {
14118       if (level.block_snap_field)
14119         setFieldForSnapping(x, y, element, move_direction);
14120       else
14121         TestIfElementTouchesCustomElement(x, y);        // for empty space
14122
14123       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14124                                           player->index_bit, dig_side);
14125     }
14126   }
14127   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14128   {
14129     if (mode == DF_SNAP && element != EL_BD_ROCK)
14130       return MP_NO_ACTION;
14131
14132     if (CAN_FALL(element) && dy)
14133       return MP_NO_ACTION;
14134
14135     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14136         !(element == EL_SPRING && level.use_spring_bug))
14137       return MP_NO_ACTION;
14138
14139     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14140         ((move_direction & MV_VERTICAL &&
14141           ((element_info[element].move_pattern & MV_LEFT &&
14142             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14143            (element_info[element].move_pattern & MV_RIGHT &&
14144             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14145          (move_direction & MV_HORIZONTAL &&
14146           ((element_info[element].move_pattern & MV_UP &&
14147             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14148            (element_info[element].move_pattern & MV_DOWN &&
14149             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14150       return MP_NO_ACTION;
14151
14152     // do not push elements already moving away faster than player
14153     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14154         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14155       return MP_NO_ACTION;
14156
14157     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14158     {
14159       if (player->push_delay_value == -1 || !player_was_pushing)
14160         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14161     }
14162     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14163     {
14164       if (player->push_delay_value == -1)
14165         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14166     }
14167     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14168     {
14169       if (!player->is_pushing)
14170         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14171     }
14172
14173     player->is_pushing = TRUE;
14174     player->is_active = TRUE;
14175
14176     if (!(IN_LEV_FIELD(nextx, nexty) &&
14177           (IS_FREE(nextx, nexty) ||
14178            (IS_SB_ELEMENT(element) &&
14179             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14180            (IS_CUSTOM_ELEMENT(element) &&
14181             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14182       return MP_NO_ACTION;
14183
14184     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14185       return MP_NO_ACTION;
14186
14187     if (player->push_delay == -1)       // new pushing; restart delay
14188       player->push_delay = 0;
14189
14190     if (player->push_delay < player->push_delay_value &&
14191         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14192         element != EL_SPRING && element != EL_BALLOON)
14193     {
14194       // make sure that there is no move delay before next try to push
14195       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14196         player->move_delay = 0;
14197
14198       return MP_NO_ACTION;
14199     }
14200
14201     if (IS_CUSTOM_ELEMENT(element) &&
14202         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14203     {
14204       if (!DigFieldByCE(nextx, nexty, element))
14205         return MP_NO_ACTION;
14206     }
14207
14208     if (IS_SB_ELEMENT(element))
14209     {
14210       boolean sokoban_task_solved = FALSE;
14211
14212       if (element == EL_SOKOBAN_FIELD_FULL)
14213       {
14214         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14215
14216         IncrementSokobanFieldsNeeded();
14217         IncrementSokobanObjectsNeeded();
14218       }
14219
14220       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14221       {
14222         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14223
14224         DecrementSokobanFieldsNeeded();
14225         DecrementSokobanObjectsNeeded();
14226
14227         // sokoban object was pushed from empty field to sokoban field
14228         if (Back[x][y] == EL_EMPTY)
14229           sokoban_task_solved = TRUE;
14230       }
14231
14232       Tile[x][y] = EL_SOKOBAN_OBJECT;
14233
14234       if (Back[x][y] == Back[nextx][nexty])
14235         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14236       else if (Back[x][y] != 0)
14237         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14238                                     ACTION_EMPTYING);
14239       else
14240         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14241                                     ACTION_FILLING);
14242
14243       if (sokoban_task_solved &&
14244           game.sokoban_fields_still_needed == 0 &&
14245           game.sokoban_objects_still_needed == 0 &&
14246           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14247       {
14248         game.players_still_needed = 0;
14249
14250         LevelSolved();
14251
14252         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14253       }
14254     }
14255     else
14256       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14257
14258     InitMovingField(x, y, move_direction);
14259     GfxAction[x][y] = ACTION_PUSHING;
14260
14261     if (mode == DF_SNAP)
14262       ContinueMoving(x, y);
14263     else
14264       MovPos[x][y] = (dx != 0 ? dx : dy);
14265
14266     Pushed[x][y] = TRUE;
14267     Pushed[nextx][nexty] = TRUE;
14268
14269     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14270       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14271     else
14272       player->push_delay_value = -1;    // get new value later
14273
14274     // check for element change _after_ element has been pushed
14275     if (game.use_change_when_pushing_bug)
14276     {
14277       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14278                                  player->index_bit, dig_side);
14279       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14280                                           player->index_bit, dig_side);
14281     }
14282   }
14283   else if (IS_SWITCHABLE(element))
14284   {
14285     if (PLAYER_SWITCHING(player, x, y))
14286     {
14287       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14288                                           player->index_bit, dig_side);
14289
14290       return MP_ACTION;
14291     }
14292
14293     player->is_switching = TRUE;
14294     player->switch_x = x;
14295     player->switch_y = y;
14296
14297     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14298
14299     if (element == EL_ROBOT_WHEEL)
14300     {
14301       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14302
14303       game.robot_wheel_x = x;
14304       game.robot_wheel_y = y;
14305       game.robot_wheel_active = TRUE;
14306
14307       TEST_DrawLevelField(x, y);
14308     }
14309     else if (element == EL_SP_TERMINAL)
14310     {
14311       int xx, yy;
14312
14313       SCAN_PLAYFIELD(xx, yy)
14314       {
14315         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14316         {
14317           Bang(xx, yy);
14318         }
14319         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14320         {
14321           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14322
14323           ResetGfxAnimation(xx, yy);
14324           TEST_DrawLevelField(xx, yy);
14325         }
14326       }
14327     }
14328     else if (IS_BELT_SWITCH(element))
14329     {
14330       ToggleBeltSwitch(x, y);
14331     }
14332     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14333              element == EL_SWITCHGATE_SWITCH_DOWN ||
14334              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14335              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14336     {
14337       ToggleSwitchgateSwitch(x, y);
14338     }
14339     else if (element == EL_LIGHT_SWITCH ||
14340              element == EL_LIGHT_SWITCH_ACTIVE)
14341     {
14342       ToggleLightSwitch(x, y);
14343     }
14344     else if (element == EL_TIMEGATE_SWITCH ||
14345              element == EL_DC_TIMEGATE_SWITCH)
14346     {
14347       ActivateTimegateSwitch(x, y);
14348     }
14349     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14350              element == EL_BALLOON_SWITCH_RIGHT ||
14351              element == EL_BALLOON_SWITCH_UP    ||
14352              element == EL_BALLOON_SWITCH_DOWN  ||
14353              element == EL_BALLOON_SWITCH_NONE  ||
14354              element == EL_BALLOON_SWITCH_ANY)
14355     {
14356       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14357                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14358                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14359                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14360                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14361                              move_direction);
14362     }
14363     else if (element == EL_LAMP)
14364     {
14365       Tile[x][y] = EL_LAMP_ACTIVE;
14366       game.lights_still_needed--;
14367
14368       ResetGfxAnimation(x, y);
14369       TEST_DrawLevelField(x, y);
14370     }
14371     else if (element == EL_TIME_ORB_FULL)
14372     {
14373       Tile[x][y] = EL_TIME_ORB_EMPTY;
14374
14375       if (level.time > 0 || level.use_time_orb_bug)
14376       {
14377         TimeLeft += level.time_orb_time;
14378         game.no_time_limit = FALSE;
14379
14380         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14381
14382         DisplayGameControlValues();
14383       }
14384
14385       ResetGfxAnimation(x, y);
14386       TEST_DrawLevelField(x, y);
14387     }
14388     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14389              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14390     {
14391       int xx, yy;
14392
14393       game.ball_active = !game.ball_active;
14394
14395       SCAN_PLAYFIELD(xx, yy)
14396       {
14397         int e = Tile[xx][yy];
14398
14399         if (game.ball_active)
14400         {
14401           if (e == EL_EMC_MAGIC_BALL)
14402             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14403           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14404             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14405         }
14406         else
14407         {
14408           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14409             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14410           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14411             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14412         }
14413       }
14414     }
14415
14416     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14417                                         player->index_bit, dig_side);
14418
14419     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14420                                         player->index_bit, dig_side);
14421
14422     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14423                                         player->index_bit, dig_side);
14424
14425     return MP_ACTION;
14426   }
14427   else
14428   {
14429     if (!PLAYER_SWITCHING(player, x, y))
14430     {
14431       player->is_switching = TRUE;
14432       player->switch_x = x;
14433       player->switch_y = y;
14434
14435       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14436                                  player->index_bit, dig_side);
14437       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14438                                           player->index_bit, dig_side);
14439
14440       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14441                                  player->index_bit, dig_side);
14442       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14443                                           player->index_bit, dig_side);
14444     }
14445
14446     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14447                                player->index_bit, dig_side);
14448     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14449                                         player->index_bit, dig_side);
14450
14451     return MP_NO_ACTION;
14452   }
14453
14454   player->push_delay = -1;
14455
14456   if (is_player)                // function can also be called by EL_PENGUIN
14457   {
14458     if (Tile[x][y] != element)          // really digged/collected something
14459     {
14460       player->is_collecting = !player->is_digging;
14461       player->is_active = TRUE;
14462     }
14463   }
14464
14465   return MP_MOVING;
14466 }
14467
14468 static boolean DigFieldByCE(int x, int y, int digging_element)
14469 {
14470   int element = Tile[x][y];
14471
14472   if (!IS_FREE(x, y))
14473   {
14474     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14475                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14476                   ACTION_BREAKING);
14477
14478     // no element can dig solid indestructible elements
14479     if (IS_INDESTRUCTIBLE(element) &&
14480         !IS_DIGGABLE(element) &&
14481         !IS_COLLECTIBLE(element))
14482       return FALSE;
14483
14484     if (AmoebaNr[x][y] &&
14485         (element == EL_AMOEBA_FULL ||
14486          element == EL_BD_AMOEBA ||
14487          element == EL_AMOEBA_GROWING))
14488     {
14489       AmoebaCnt[AmoebaNr[x][y]]--;
14490       AmoebaCnt2[AmoebaNr[x][y]]--;
14491     }
14492
14493     if (IS_MOVING(x, y))
14494       RemoveMovingField(x, y);
14495     else
14496     {
14497       RemoveField(x, y);
14498       TEST_DrawLevelField(x, y);
14499     }
14500
14501     // if digged element was about to explode, prevent the explosion
14502     ExplodeField[x][y] = EX_TYPE_NONE;
14503
14504     PlayLevelSoundAction(x, y, action);
14505   }
14506
14507   Store[x][y] = EL_EMPTY;
14508
14509   // this makes it possible to leave the removed element again
14510   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14511     Store[x][y] = element;
14512
14513   return TRUE;
14514 }
14515
14516 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14517 {
14518   int jx = player->jx, jy = player->jy;
14519   int x = jx + dx, y = jy + dy;
14520   int snap_direction = (dx == -1 ? MV_LEFT  :
14521                         dx == +1 ? MV_RIGHT :
14522                         dy == -1 ? MV_UP    :
14523                         dy == +1 ? MV_DOWN  : MV_NONE);
14524   boolean can_continue_snapping = (level.continuous_snapping &&
14525                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14526
14527   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14528     return FALSE;
14529
14530   if (!player->active || !IN_LEV_FIELD(x, y))
14531     return FALSE;
14532
14533   if (dx && dy)
14534     return FALSE;
14535
14536   if (!dx && !dy)
14537   {
14538     if (player->MovPos == 0)
14539       player->is_pushing = FALSE;
14540
14541     player->is_snapping = FALSE;
14542
14543     if (player->MovPos == 0)
14544     {
14545       player->is_moving = FALSE;
14546       player->is_digging = FALSE;
14547       player->is_collecting = FALSE;
14548     }
14549
14550     return FALSE;
14551   }
14552
14553   // prevent snapping with already pressed snap key when not allowed
14554   if (player->is_snapping && !can_continue_snapping)
14555     return FALSE;
14556
14557   player->MovDir = snap_direction;
14558
14559   if (player->MovPos == 0)
14560   {
14561     player->is_moving = FALSE;
14562     player->is_digging = FALSE;
14563     player->is_collecting = FALSE;
14564   }
14565
14566   player->is_dropping = FALSE;
14567   player->is_dropping_pressed = FALSE;
14568   player->drop_pressed_delay = 0;
14569
14570   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14571     return FALSE;
14572
14573   player->is_snapping = TRUE;
14574   player->is_active = TRUE;
14575
14576   if (player->MovPos == 0)
14577   {
14578     player->is_moving = FALSE;
14579     player->is_digging = FALSE;
14580     player->is_collecting = FALSE;
14581   }
14582
14583   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14584     TEST_DrawLevelField(player->last_jx, player->last_jy);
14585
14586   TEST_DrawLevelField(x, y);
14587
14588   return TRUE;
14589 }
14590
14591 static boolean DropElement(struct PlayerInfo *player)
14592 {
14593   int old_element, new_element;
14594   int dropx = player->jx, dropy = player->jy;
14595   int drop_direction = player->MovDir;
14596   int drop_side = drop_direction;
14597   int drop_element = get_next_dropped_element(player);
14598
14599   /* do not drop an element on top of another element; when holding drop key
14600      pressed without moving, dropped element must move away before the next
14601      element can be dropped (this is especially important if the next element
14602      is dynamite, which can be placed on background for historical reasons) */
14603   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
14604     return MP_ACTION;
14605
14606   if (IS_THROWABLE(drop_element))
14607   {
14608     dropx += GET_DX_FROM_DIR(drop_direction);
14609     dropy += GET_DY_FROM_DIR(drop_direction);
14610
14611     if (!IN_LEV_FIELD(dropx, dropy))
14612       return FALSE;
14613   }
14614
14615   old_element = Tile[dropx][dropy];     // old element at dropping position
14616   new_element = drop_element;           // default: no change when dropping
14617
14618   // check if player is active, not moving and ready to drop
14619   if (!player->active || player->MovPos || player->drop_delay > 0)
14620     return FALSE;
14621
14622   // check if player has anything that can be dropped
14623   if (new_element == EL_UNDEFINED)
14624     return FALSE;
14625
14626   // only set if player has anything that can be dropped
14627   player->is_dropping_pressed = TRUE;
14628
14629   // check if drop key was pressed long enough for EM style dynamite
14630   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14631     return FALSE;
14632
14633   // check if anything can be dropped at the current position
14634   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14635     return FALSE;
14636
14637   // collected custom elements can only be dropped on empty fields
14638   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14639     return FALSE;
14640
14641   if (old_element != EL_EMPTY)
14642     Back[dropx][dropy] = old_element;   // store old element on this field
14643
14644   ResetGfxAnimation(dropx, dropy);
14645   ResetRandomAnimationValue(dropx, dropy);
14646
14647   if (player->inventory_size > 0 ||
14648       player->inventory_infinite_element != EL_UNDEFINED)
14649   {
14650     if (player->inventory_size > 0)
14651     {
14652       player->inventory_size--;
14653
14654       DrawGameDoorValues();
14655
14656       if (new_element == EL_DYNAMITE)
14657         new_element = EL_DYNAMITE_ACTIVE;
14658       else if (new_element == EL_EM_DYNAMITE)
14659         new_element = EL_EM_DYNAMITE_ACTIVE;
14660       else if (new_element == EL_SP_DISK_RED)
14661         new_element = EL_SP_DISK_RED_ACTIVE;
14662     }
14663
14664     Tile[dropx][dropy] = new_element;
14665
14666     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14667       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14668                           el2img(Tile[dropx][dropy]), 0);
14669
14670     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14671
14672     // needed if previous element just changed to "empty" in the last frame
14673     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14674
14675     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14676                                player->index_bit, drop_side);
14677     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14678                                         CE_PLAYER_DROPS_X,
14679                                         player->index_bit, drop_side);
14680
14681     TestIfElementTouchesCustomElement(dropx, dropy);
14682   }
14683   else          // player is dropping a dyna bomb
14684   {
14685     player->dynabombs_left--;
14686
14687     Tile[dropx][dropy] = new_element;
14688
14689     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14690       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14691                           el2img(Tile[dropx][dropy]), 0);
14692
14693     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14694   }
14695
14696   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
14697     InitField_WithBug1(dropx, dropy, FALSE);
14698
14699   new_element = Tile[dropx][dropy];     // element might have changed
14700
14701   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14702       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14703   {
14704     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14705       MovDir[dropx][dropy] = drop_direction;
14706
14707     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14708
14709     // do not cause impact style collision by dropping elements that can fall
14710     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14711   }
14712
14713   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14714   player->is_dropping = TRUE;
14715
14716   player->drop_pressed_delay = 0;
14717   player->is_dropping_pressed = FALSE;
14718
14719   player->drop_x = dropx;
14720   player->drop_y = dropy;
14721
14722   return TRUE;
14723 }
14724
14725 // ----------------------------------------------------------------------------
14726 // game sound playing functions
14727 // ----------------------------------------------------------------------------
14728
14729 static int *loop_sound_frame = NULL;
14730 static int *loop_sound_volume = NULL;
14731
14732 void InitPlayLevelSound(void)
14733 {
14734   int num_sounds = getSoundListSize();
14735
14736   checked_free(loop_sound_frame);
14737   checked_free(loop_sound_volume);
14738
14739   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14740   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14741 }
14742
14743 static void PlayLevelSound(int x, int y, int nr)
14744 {
14745   int sx = SCREENX(x), sy = SCREENY(y);
14746   int volume, stereo_position;
14747   int max_distance = 8;
14748   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14749
14750   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14751       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14752     return;
14753
14754   if (!IN_LEV_FIELD(x, y) ||
14755       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14756       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14757     return;
14758
14759   volume = SOUND_MAX_VOLUME;
14760
14761   if (!IN_SCR_FIELD(sx, sy))
14762   {
14763     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14764     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14765
14766     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14767   }
14768
14769   stereo_position = (SOUND_MAX_LEFT +
14770                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14771                      (SCR_FIELDX + 2 * max_distance));
14772
14773   if (IS_LOOP_SOUND(nr))
14774   {
14775     /* This assures that quieter loop sounds do not overwrite louder ones,
14776        while restarting sound volume comparison with each new game frame. */
14777
14778     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14779       return;
14780
14781     loop_sound_volume[nr] = volume;
14782     loop_sound_frame[nr] = FrameCounter;
14783   }
14784
14785   PlaySoundExt(nr, volume, stereo_position, type);
14786 }
14787
14788 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14789 {
14790   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14791                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14792                  y < LEVELY(BY1) ? LEVELY(BY1) :
14793                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14794                  sound_action);
14795 }
14796
14797 static void PlayLevelSoundAction(int x, int y, int action)
14798 {
14799   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
14800 }
14801
14802 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14803 {
14804   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14805
14806   if (sound_effect != SND_UNDEFINED)
14807     PlayLevelSound(x, y, sound_effect);
14808 }
14809
14810 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14811                                               int action)
14812 {
14813   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14814
14815   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14816     PlayLevelSound(x, y, sound_effect);
14817 }
14818
14819 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14820 {
14821   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].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 StopLevelSoundActionIfLoop(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     StopSound(sound_effect);
14833 }
14834
14835 static int getLevelMusicNr(void)
14836 {
14837   if (levelset.music[level_nr] != MUS_UNDEFINED)
14838     return levelset.music[level_nr];            // from config file
14839   else
14840     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
14841 }
14842
14843 static void FadeLevelSounds(void)
14844 {
14845   FadeSounds();
14846 }
14847
14848 static void FadeLevelMusic(void)
14849 {
14850   int music_nr = getLevelMusicNr();
14851   char *curr_music = getCurrentlyPlayingMusicFilename();
14852   char *next_music = getMusicInfoEntryFilename(music_nr);
14853
14854   if (!strEqual(curr_music, next_music))
14855     FadeMusic();
14856 }
14857
14858 void FadeLevelSoundsAndMusic(void)
14859 {
14860   FadeLevelSounds();
14861   FadeLevelMusic();
14862 }
14863
14864 static void PlayLevelMusic(void)
14865 {
14866   int music_nr = getLevelMusicNr();
14867   char *curr_music = getCurrentlyPlayingMusicFilename();
14868   char *next_music = getMusicInfoEntryFilename(music_nr);
14869
14870   if (!strEqual(curr_music, next_music))
14871     PlayMusicLoop(music_nr);
14872 }
14873
14874 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14875 {
14876   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
14877   int offset = 0;
14878   int x = xx - offset;
14879   int y = yy - offset;
14880
14881   switch (sample)
14882   {
14883     case SOUND_blank:
14884       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14885       break;
14886
14887     case SOUND_roll:
14888       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14889       break;
14890
14891     case SOUND_stone:
14892       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14893       break;
14894
14895     case SOUND_nut:
14896       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14897       break;
14898
14899     case SOUND_crack:
14900       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14901       break;
14902
14903     case SOUND_bug:
14904       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14905       break;
14906
14907     case SOUND_tank:
14908       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14909       break;
14910
14911     case SOUND_android_clone:
14912       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14913       break;
14914
14915     case SOUND_android_move:
14916       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14917       break;
14918
14919     case SOUND_spring:
14920       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14921       break;
14922
14923     case SOUND_slurp:
14924       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14925       break;
14926
14927     case SOUND_eater:
14928       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14929       break;
14930
14931     case SOUND_eater_eat:
14932       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14933       break;
14934
14935     case SOUND_alien:
14936       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14937       break;
14938
14939     case SOUND_collect:
14940       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14941       break;
14942
14943     case SOUND_diamond:
14944       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14945       break;
14946
14947     case SOUND_squash:
14948       // !!! CHECK THIS !!!
14949 #if 1
14950       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14951 #else
14952       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14953 #endif
14954       break;
14955
14956     case SOUND_wonderfall:
14957       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14958       break;
14959
14960     case SOUND_drip:
14961       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14962       break;
14963
14964     case SOUND_push:
14965       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14966       break;
14967
14968     case SOUND_dirt:
14969       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14970       break;
14971
14972     case SOUND_acid:
14973       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14974       break;
14975
14976     case SOUND_ball:
14977       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14978       break;
14979
14980     case SOUND_slide:
14981       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14982       break;
14983
14984     case SOUND_wonder:
14985       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14986       break;
14987
14988     case SOUND_door:
14989       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14990       break;
14991
14992     case SOUND_exit_open:
14993       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14994       break;
14995
14996     case SOUND_exit_leave:
14997       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14998       break;
14999
15000     case SOUND_dynamite:
15001       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15002       break;
15003
15004     case SOUND_tick:
15005       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15006       break;
15007
15008     case SOUND_press:
15009       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15010       break;
15011
15012     case SOUND_wheel:
15013       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15014       break;
15015
15016     case SOUND_boom:
15017       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15018       break;
15019
15020     case SOUND_die:
15021       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15022       break;
15023
15024     case SOUND_time:
15025       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15026       break;
15027
15028     default:
15029       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15030       break;
15031   }
15032 }
15033
15034 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15035 {
15036   int element = map_element_SP_to_RND(element_sp);
15037   int action = map_action_SP_to_RND(action_sp);
15038   int offset = (setup.sp_show_border_elements ? 0 : 1);
15039   int x = xx - offset;
15040   int y = yy - offset;
15041
15042   PlayLevelSoundElementAction(x, y, element, action);
15043 }
15044
15045 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15046 {
15047   int element = map_element_MM_to_RND(element_mm);
15048   int action = map_action_MM_to_RND(action_mm);
15049   int offset = 0;
15050   int x = xx - offset;
15051   int y = yy - offset;
15052
15053   if (!IS_MM_ELEMENT(element))
15054     element = EL_MM_DEFAULT;
15055
15056   PlayLevelSoundElementAction(x, y, element, action);
15057 }
15058
15059 void PlaySound_MM(int sound_mm)
15060 {
15061   int sound = map_sound_MM_to_RND(sound_mm);
15062
15063   if (sound == SND_UNDEFINED)
15064     return;
15065
15066   PlaySound(sound);
15067 }
15068
15069 void PlaySoundLoop_MM(int sound_mm)
15070 {
15071   int sound = map_sound_MM_to_RND(sound_mm);
15072
15073   if (sound == SND_UNDEFINED)
15074     return;
15075
15076   PlaySoundLoop(sound);
15077 }
15078
15079 void StopSound_MM(int sound_mm)
15080 {
15081   int sound = map_sound_MM_to_RND(sound_mm);
15082
15083   if (sound == SND_UNDEFINED)
15084     return;
15085
15086   StopSound(sound);
15087 }
15088
15089 void RaiseScore(int value)
15090 {
15091   game.score += value;
15092
15093   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15094
15095   DisplayGameControlValues();
15096 }
15097
15098 void RaiseScoreElement(int element)
15099 {
15100   switch (element)
15101   {
15102     case EL_EMERALD:
15103     case EL_BD_DIAMOND:
15104     case EL_EMERALD_YELLOW:
15105     case EL_EMERALD_RED:
15106     case EL_EMERALD_PURPLE:
15107     case EL_SP_INFOTRON:
15108       RaiseScore(level.score[SC_EMERALD]);
15109       break;
15110     case EL_DIAMOND:
15111       RaiseScore(level.score[SC_DIAMOND]);
15112       break;
15113     case EL_CRYSTAL:
15114       RaiseScore(level.score[SC_CRYSTAL]);
15115       break;
15116     case EL_PEARL:
15117       RaiseScore(level.score[SC_PEARL]);
15118       break;
15119     case EL_BUG:
15120     case EL_BD_BUTTERFLY:
15121     case EL_SP_ELECTRON:
15122       RaiseScore(level.score[SC_BUG]);
15123       break;
15124     case EL_SPACESHIP:
15125     case EL_BD_FIREFLY:
15126     case EL_SP_SNIKSNAK:
15127       RaiseScore(level.score[SC_SPACESHIP]);
15128       break;
15129     case EL_YAMYAM:
15130     case EL_DARK_YAMYAM:
15131       RaiseScore(level.score[SC_YAMYAM]);
15132       break;
15133     case EL_ROBOT:
15134       RaiseScore(level.score[SC_ROBOT]);
15135       break;
15136     case EL_PACMAN:
15137       RaiseScore(level.score[SC_PACMAN]);
15138       break;
15139     case EL_NUT:
15140       RaiseScore(level.score[SC_NUT]);
15141       break;
15142     case EL_DYNAMITE:
15143     case EL_EM_DYNAMITE:
15144     case EL_SP_DISK_RED:
15145     case EL_DYNABOMB_INCREASE_NUMBER:
15146     case EL_DYNABOMB_INCREASE_SIZE:
15147     case EL_DYNABOMB_INCREASE_POWER:
15148       RaiseScore(level.score[SC_DYNAMITE]);
15149       break;
15150     case EL_SHIELD_NORMAL:
15151     case EL_SHIELD_DEADLY:
15152       RaiseScore(level.score[SC_SHIELD]);
15153       break;
15154     case EL_EXTRA_TIME:
15155       RaiseScore(level.extra_time_score);
15156       break;
15157     case EL_KEY_1:
15158     case EL_KEY_2:
15159     case EL_KEY_3:
15160     case EL_KEY_4:
15161     case EL_EM_KEY_1:
15162     case EL_EM_KEY_2:
15163     case EL_EM_KEY_3:
15164     case EL_EM_KEY_4:
15165     case EL_EMC_KEY_5:
15166     case EL_EMC_KEY_6:
15167     case EL_EMC_KEY_7:
15168     case EL_EMC_KEY_8:
15169     case EL_DC_KEY_WHITE:
15170       RaiseScore(level.score[SC_KEY]);
15171       break;
15172     default:
15173       RaiseScore(element_info[element].collect_score);
15174       break;
15175   }
15176 }
15177
15178 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15179 {
15180   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15181   {
15182     // closing door required in case of envelope style request dialogs
15183     if (!skip_request)
15184     {
15185       // prevent short reactivation of overlay buttons while closing door
15186       SetOverlayActive(FALSE);
15187
15188       CloseDoor(DOOR_CLOSE_1);
15189     }
15190
15191     if (network.enabled)
15192       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15193     else
15194     {
15195       if (quick_quit)
15196         FadeSkipNextFadeIn();
15197
15198       SetGameStatus(GAME_MODE_MAIN);
15199
15200       DrawMainMenu();
15201     }
15202   }
15203   else          // continue playing the game
15204   {
15205     if (tape.playing && tape.deactivate_display)
15206       TapeDeactivateDisplayOff(TRUE);
15207
15208     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15209
15210     if (tape.playing && tape.deactivate_display)
15211       TapeDeactivateDisplayOn();
15212   }
15213 }
15214
15215 void RequestQuitGame(boolean ask_if_really_quit)
15216 {
15217   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15218   boolean skip_request = game.all_players_gone || quick_quit;
15219
15220   RequestQuitGameExt(skip_request, quick_quit,
15221                      "Do you really want to quit the game?");
15222 }
15223
15224 void RequestRestartGame(char *message)
15225 {
15226   game.restart_game_message = NULL;
15227
15228   boolean has_started_game = hasStartedNetworkGame();
15229   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15230
15231   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15232   {
15233     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15234   }
15235   else
15236   {
15237     SetGameStatus(GAME_MODE_MAIN);
15238
15239     DrawMainMenu();
15240   }
15241 }
15242
15243 void CheckGameOver(void)
15244 {
15245   static boolean last_game_over = FALSE;
15246   static int game_over_delay = 0;
15247   int game_over_delay_value = 50;
15248   boolean game_over = checkGameFailed();
15249
15250   // do not handle game over if request dialog is already active
15251   if (game.request_active)
15252     return;
15253
15254   // do not ask to play again if game was never actually played
15255   if (!game.GamePlayed)
15256     return;
15257
15258   if (!game_over)
15259   {
15260     last_game_over = FALSE;
15261     game_over_delay = game_over_delay_value;
15262
15263     return;
15264   }
15265
15266   if (game_over_delay > 0)
15267   {
15268     game_over_delay--;
15269
15270     return;
15271   }
15272
15273   if (last_game_over != game_over)
15274     game.restart_game_message = (hasStartedNetworkGame() ?
15275                                  "Game over! Play it again?" :
15276                                  "Game over!");
15277
15278   last_game_over = game_over;
15279 }
15280
15281 boolean checkGameSolved(void)
15282 {
15283   // set for all game engines if level was solved
15284   return game.LevelSolved_GameEnd;
15285 }
15286
15287 boolean checkGameFailed(void)
15288 {
15289   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15290     return (game_em.game_over && !game_em.level_solved);
15291   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15292     return (game_sp.game_over && !game_sp.level_solved);
15293   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15294     return (game_mm.game_over && !game_mm.level_solved);
15295   else                          // GAME_ENGINE_TYPE_RND
15296     return (game.GameOver && !game.LevelSolved);
15297 }
15298
15299 boolean checkGameEnded(void)
15300 {
15301   return (checkGameSolved() || checkGameFailed());
15302 }
15303
15304
15305 // ----------------------------------------------------------------------------
15306 // random generator functions
15307 // ----------------------------------------------------------------------------
15308
15309 unsigned int InitEngineRandom_RND(int seed)
15310 {
15311   game.num_random_calls = 0;
15312
15313   return InitEngineRandom(seed);
15314 }
15315
15316 unsigned int RND(int max)
15317 {
15318   if (max > 0)
15319   {
15320     game.num_random_calls++;
15321
15322     return GetEngineRandom(max);
15323   }
15324
15325   return 0;
15326 }
15327
15328
15329 // ----------------------------------------------------------------------------
15330 // game engine snapshot handling functions
15331 // ----------------------------------------------------------------------------
15332
15333 struct EngineSnapshotInfo
15334 {
15335   // runtime values for custom element collect score
15336   int collect_score[NUM_CUSTOM_ELEMENTS];
15337
15338   // runtime values for group element choice position
15339   int choice_pos[NUM_GROUP_ELEMENTS];
15340
15341   // runtime values for belt position animations
15342   int belt_graphic[4][NUM_BELT_PARTS];
15343   int belt_anim_mode[4][NUM_BELT_PARTS];
15344 };
15345
15346 static struct EngineSnapshotInfo engine_snapshot_rnd;
15347 static char *snapshot_level_identifier = NULL;
15348 static int snapshot_level_nr = -1;
15349
15350 static void SaveEngineSnapshotValues_RND(void)
15351 {
15352   static int belt_base_active_element[4] =
15353   {
15354     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15355     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15356     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15357     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15358   };
15359   int i, j;
15360
15361   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15362   {
15363     int element = EL_CUSTOM_START + i;
15364
15365     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15366   }
15367
15368   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15369   {
15370     int element = EL_GROUP_START + i;
15371
15372     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15373   }
15374
15375   for (i = 0; i < 4; i++)
15376   {
15377     for (j = 0; j < NUM_BELT_PARTS; j++)
15378     {
15379       int element = belt_base_active_element[i] + j;
15380       int graphic = el2img(element);
15381       int anim_mode = graphic_info[graphic].anim_mode;
15382
15383       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15384       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15385     }
15386   }
15387 }
15388
15389 static void LoadEngineSnapshotValues_RND(void)
15390 {
15391   unsigned int num_random_calls = game.num_random_calls;
15392   int i, j;
15393
15394   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15395   {
15396     int element = EL_CUSTOM_START + i;
15397
15398     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15399   }
15400
15401   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15402   {
15403     int element = EL_GROUP_START + i;
15404
15405     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15406   }
15407
15408   for (i = 0; i < 4; i++)
15409   {
15410     for (j = 0; j < NUM_BELT_PARTS; j++)
15411     {
15412       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15413       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15414
15415       graphic_info[graphic].anim_mode = anim_mode;
15416     }
15417   }
15418
15419   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15420   {
15421     InitRND(tape.random_seed);
15422     for (i = 0; i < num_random_calls; i++)
15423       RND(1);
15424   }
15425
15426   if (game.num_random_calls != num_random_calls)
15427   {
15428     Error("number of random calls out of sync");
15429     Error("number of random calls should be %d", num_random_calls);
15430     Error("number of random calls is %d", game.num_random_calls);
15431
15432     Fail("this should not happen -- please debug");
15433   }
15434 }
15435
15436 void FreeEngineSnapshotSingle(void)
15437 {
15438   FreeSnapshotSingle();
15439
15440   setString(&snapshot_level_identifier, NULL);
15441   snapshot_level_nr = -1;
15442 }
15443
15444 void FreeEngineSnapshotList(void)
15445 {
15446   FreeSnapshotList();
15447 }
15448
15449 static ListNode *SaveEngineSnapshotBuffers(void)
15450 {
15451   ListNode *buffers = NULL;
15452
15453   // copy some special values to a structure better suited for the snapshot
15454
15455   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15456     SaveEngineSnapshotValues_RND();
15457   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15458     SaveEngineSnapshotValues_EM();
15459   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15460     SaveEngineSnapshotValues_SP(&buffers);
15461   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15462     SaveEngineSnapshotValues_MM(&buffers);
15463
15464   // save values stored in special snapshot structure
15465
15466   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15467     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15468   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15469     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15470   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15471     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15472   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15473     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15474
15475   // save further RND engine values
15476
15477   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15478   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15479   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15480
15481   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15482   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15483   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15484   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15485   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15486
15487   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15488   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15489   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15490
15491   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15492
15493   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15494   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15495
15496   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15497   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15498   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15499   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15500   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15501   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15502   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15503   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15504   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15505   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15506   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15507   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15508   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15509   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15510   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15511   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15512   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15513   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15514
15515   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15516   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15517
15518   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15519   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15520   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15521
15522   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15523   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15524
15525   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15526   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15527   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15528   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15529   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15530
15531   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15532   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15533
15534 #if 0
15535   ListNode *node = engine_snapshot_list_rnd;
15536   int num_bytes = 0;
15537
15538   while (node != NULL)
15539   {
15540     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15541
15542     node = node->next;
15543   }
15544
15545   Debug("game:playing:SaveEngineSnapshotBuffers",
15546         "size of engine snapshot: %d bytes", num_bytes);
15547 #endif
15548
15549   return buffers;
15550 }
15551
15552 void SaveEngineSnapshotSingle(void)
15553 {
15554   ListNode *buffers = SaveEngineSnapshotBuffers();
15555
15556   // finally save all snapshot buffers to single snapshot
15557   SaveSnapshotSingle(buffers);
15558
15559   // save level identification information
15560   setString(&snapshot_level_identifier, leveldir_current->identifier);
15561   snapshot_level_nr = level_nr;
15562 }
15563
15564 boolean CheckSaveEngineSnapshotToList(void)
15565 {
15566   boolean save_snapshot =
15567     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15568      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15569       game.snapshot.changed_action) ||
15570      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15571       game.snapshot.collected_item));
15572
15573   game.snapshot.changed_action = FALSE;
15574   game.snapshot.collected_item = FALSE;
15575   game.snapshot.save_snapshot = save_snapshot;
15576
15577   return save_snapshot;
15578 }
15579
15580 void SaveEngineSnapshotToList(void)
15581 {
15582   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15583       tape.quick_resume)
15584     return;
15585
15586   ListNode *buffers = SaveEngineSnapshotBuffers();
15587
15588   // finally save all snapshot buffers to snapshot list
15589   SaveSnapshotToList(buffers);
15590 }
15591
15592 void SaveEngineSnapshotToListInitial(void)
15593 {
15594   FreeEngineSnapshotList();
15595
15596   SaveEngineSnapshotToList();
15597 }
15598
15599 static void LoadEngineSnapshotValues(void)
15600 {
15601   // restore special values from snapshot structure
15602
15603   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15604     LoadEngineSnapshotValues_RND();
15605   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15606     LoadEngineSnapshotValues_EM();
15607   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15608     LoadEngineSnapshotValues_SP();
15609   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15610     LoadEngineSnapshotValues_MM();
15611 }
15612
15613 void LoadEngineSnapshotSingle(void)
15614 {
15615   LoadSnapshotSingle();
15616
15617   LoadEngineSnapshotValues();
15618 }
15619
15620 static void LoadEngineSnapshot_Undo(int steps)
15621 {
15622   LoadSnapshotFromList_Older(steps);
15623
15624   LoadEngineSnapshotValues();
15625 }
15626
15627 static void LoadEngineSnapshot_Redo(int steps)
15628 {
15629   LoadSnapshotFromList_Newer(steps);
15630
15631   LoadEngineSnapshotValues();
15632 }
15633
15634 boolean CheckEngineSnapshotSingle(void)
15635 {
15636   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15637           snapshot_level_nr == level_nr);
15638 }
15639
15640 boolean CheckEngineSnapshotList(void)
15641 {
15642   return CheckSnapshotList();
15643 }
15644
15645
15646 // ---------- new game button stuff -------------------------------------------
15647
15648 static struct
15649 {
15650   int graphic;
15651   struct XY *pos;
15652   int gadget_id;
15653   boolean *setup_value;
15654   boolean allowed_on_tape;
15655   boolean is_touch_button;
15656   char *infotext;
15657 } gamebutton_info[NUM_GAME_BUTTONS] =
15658 {
15659   {
15660     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15661     GAME_CTRL_ID_STOP,                          NULL,
15662     TRUE, FALSE,                                "stop game"
15663   },
15664   {
15665     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15666     GAME_CTRL_ID_PAUSE,                         NULL,
15667     TRUE, FALSE,                                "pause game"
15668   },
15669   {
15670     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15671     GAME_CTRL_ID_PLAY,                          NULL,
15672     TRUE, FALSE,                                "play game"
15673   },
15674   {
15675     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15676     GAME_CTRL_ID_UNDO,                          NULL,
15677     TRUE, FALSE,                                "undo step"
15678   },
15679   {
15680     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15681     GAME_CTRL_ID_REDO,                          NULL,
15682     TRUE, FALSE,                                "redo step"
15683   },
15684   {
15685     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15686     GAME_CTRL_ID_SAVE,                          NULL,
15687     TRUE, FALSE,                                "save game"
15688   },
15689   {
15690     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15691     GAME_CTRL_ID_PAUSE2,                        NULL,
15692     TRUE, FALSE,                                "pause game"
15693   },
15694   {
15695     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15696     GAME_CTRL_ID_LOAD,                          NULL,
15697     TRUE, FALSE,                                "load game"
15698   },
15699   {
15700     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15701     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15702     FALSE, FALSE,                               "stop game"
15703   },
15704   {
15705     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15706     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15707     FALSE, FALSE,                               "pause game"
15708   },
15709   {
15710     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15711     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15712     FALSE, FALSE,                               "play game"
15713   },
15714   {
15715     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
15716     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
15717     FALSE, TRUE,                                "stop game"
15718   },
15719   {
15720     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
15721     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
15722     FALSE, TRUE,                                "pause game"
15723   },
15724   {
15725     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15726     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15727     TRUE, FALSE,                                "background music on/off"
15728   },
15729   {
15730     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15731     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15732     TRUE, FALSE,                                "sound loops on/off"
15733   },
15734   {
15735     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15736     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15737     TRUE, FALSE,                                "normal sounds on/off"
15738   },
15739   {
15740     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15741     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15742     FALSE, FALSE,                               "background music on/off"
15743   },
15744   {
15745     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15746     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15747     FALSE, FALSE,                               "sound loops on/off"
15748   },
15749   {
15750     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15751     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15752     FALSE, FALSE,                               "normal sounds on/off"
15753   }
15754 };
15755
15756 void CreateGameButtons(void)
15757 {
15758   int i;
15759
15760   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15761   {
15762     int graphic = gamebutton_info[i].graphic;
15763     struct GraphicInfo *gfx = &graphic_info[graphic];
15764     struct XY *pos = gamebutton_info[i].pos;
15765     struct GadgetInfo *gi;
15766     int button_type;
15767     boolean checked;
15768     unsigned int event_mask;
15769     boolean is_touch_button = gamebutton_info[i].is_touch_button;
15770     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15771     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15772     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
15773     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
15774     int gd_x   = gfx->src_x;
15775     int gd_y   = gfx->src_y;
15776     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15777     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15778     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15779     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15780     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15781     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15782     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
15783     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
15784     int id = i;
15785
15786     if (gfx->bitmap == NULL)
15787     {
15788       game_gadget[id] = NULL;
15789
15790       continue;
15791     }
15792
15793     if (id == GAME_CTRL_ID_STOP ||
15794         id == GAME_CTRL_ID_PANEL_STOP ||
15795         id == GAME_CTRL_ID_TOUCH_STOP ||
15796         id == GAME_CTRL_ID_PLAY ||
15797         id == GAME_CTRL_ID_PANEL_PLAY ||
15798         id == GAME_CTRL_ID_SAVE ||
15799         id == GAME_CTRL_ID_LOAD)
15800     {
15801       button_type = GD_TYPE_NORMAL_BUTTON;
15802       checked = FALSE;
15803       event_mask = GD_EVENT_RELEASED;
15804     }
15805     else if (id == GAME_CTRL_ID_UNDO ||
15806              id == GAME_CTRL_ID_REDO)
15807     {
15808       button_type = GD_TYPE_NORMAL_BUTTON;
15809       checked = FALSE;
15810       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15811     }
15812     else
15813     {
15814       button_type = GD_TYPE_CHECK_BUTTON;
15815       checked = (gamebutton_info[i].setup_value != NULL ?
15816                  *gamebutton_info[i].setup_value : FALSE);
15817       event_mask = GD_EVENT_PRESSED;
15818     }
15819
15820     gi = CreateGadget(GDI_CUSTOM_ID, id,
15821                       GDI_IMAGE_ID, graphic,
15822                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15823                       GDI_X, base_x + x,
15824                       GDI_Y, base_y + y,
15825                       GDI_WIDTH, gfx->width,
15826                       GDI_HEIGHT, gfx->height,
15827                       GDI_TYPE, button_type,
15828                       GDI_STATE, GD_BUTTON_UNPRESSED,
15829                       GDI_CHECKED, checked,
15830                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15831                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15832                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15833                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15834                       GDI_DIRECT_DRAW, FALSE,
15835                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
15836                       GDI_EVENT_MASK, event_mask,
15837                       GDI_CALLBACK_ACTION, HandleGameButtons,
15838                       GDI_END);
15839
15840     if (gi == NULL)
15841       Fail("cannot create gadget");
15842
15843     game_gadget[id] = gi;
15844   }
15845 }
15846
15847 void FreeGameButtons(void)
15848 {
15849   int i;
15850
15851   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15852     FreeGadget(game_gadget[i]);
15853 }
15854
15855 static void UnmapGameButtonsAtSamePosition(int id)
15856 {
15857   int i;
15858
15859   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15860     if (i != id &&
15861         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15862         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15863       UnmapGadget(game_gadget[i]);
15864 }
15865
15866 static void UnmapGameButtonsAtSamePosition_All(void)
15867 {
15868   if (setup.show_snapshot_buttons)
15869   {
15870     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15871     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15872     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15873   }
15874   else
15875   {
15876     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15877     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15878     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15879
15880     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15881     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15882     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15883   }
15884 }
15885
15886 static void MapGameButtonsAtSamePosition(int id)
15887 {
15888   int i;
15889
15890   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15891     if (i != id &&
15892         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15893         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15894       MapGadget(game_gadget[i]);
15895
15896   UnmapGameButtonsAtSamePosition_All();
15897 }
15898
15899 void MapUndoRedoButtons(void)
15900 {
15901   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15902   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15903
15904   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15905   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15906 }
15907
15908 void UnmapUndoRedoButtons(void)
15909 {
15910   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15911   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15912
15913   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15914   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15915 }
15916
15917 void ModifyPauseButtons(void)
15918 {
15919   static int ids[] =
15920   {
15921     GAME_CTRL_ID_PAUSE,
15922     GAME_CTRL_ID_PAUSE2,
15923     GAME_CTRL_ID_PANEL_PAUSE,
15924     GAME_CTRL_ID_TOUCH_PAUSE,
15925     -1
15926   };
15927   int i;
15928
15929   for (i = 0; ids[i] > -1; i++)
15930     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
15931 }
15932
15933 static void MapGameButtonsExt(boolean on_tape)
15934 {
15935   int i;
15936
15937   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15938     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15939         i != GAME_CTRL_ID_UNDO &&
15940         i != GAME_CTRL_ID_REDO)
15941       MapGadget(game_gadget[i]);
15942
15943   UnmapGameButtonsAtSamePosition_All();
15944
15945   RedrawGameButtons();
15946 }
15947
15948 static void UnmapGameButtonsExt(boolean on_tape)
15949 {
15950   int i;
15951
15952   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15953     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15954       UnmapGadget(game_gadget[i]);
15955 }
15956
15957 static void RedrawGameButtonsExt(boolean on_tape)
15958 {
15959   int i;
15960
15961   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15962     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15963       RedrawGadget(game_gadget[i]);
15964 }
15965
15966 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
15967 {
15968   if (gi == NULL)
15969     return;
15970
15971   gi->checked = state;
15972 }
15973
15974 static void RedrawSoundButtonGadget(int id)
15975 {
15976   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
15977              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
15978              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
15979              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
15980              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
15981              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15982              id);
15983
15984   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15985   RedrawGadget(game_gadget[id2]);
15986 }
15987
15988 void MapGameButtons(void)
15989 {
15990   MapGameButtonsExt(FALSE);
15991 }
15992
15993 void UnmapGameButtons(void)
15994 {
15995   UnmapGameButtonsExt(FALSE);
15996 }
15997
15998 void RedrawGameButtons(void)
15999 {
16000   RedrawGameButtonsExt(FALSE);
16001 }
16002
16003 void MapGameButtonsOnTape(void)
16004 {
16005   MapGameButtonsExt(TRUE);
16006 }
16007
16008 void UnmapGameButtonsOnTape(void)
16009 {
16010   UnmapGameButtonsExt(TRUE);
16011 }
16012
16013 void RedrawGameButtonsOnTape(void)
16014 {
16015   RedrawGameButtonsExt(TRUE);
16016 }
16017
16018 static void GameUndoRedoExt(void)
16019 {
16020   ClearPlayerAction();
16021
16022   tape.pausing = TRUE;
16023
16024   RedrawPlayfield();
16025   UpdateAndDisplayGameControlValues();
16026
16027   DrawCompleteVideoDisplay();
16028   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16029   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16030   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16031
16032   BackToFront();
16033 }
16034
16035 static void GameUndo(int steps)
16036 {
16037   if (!CheckEngineSnapshotList())
16038     return;
16039
16040   LoadEngineSnapshot_Undo(steps);
16041
16042   GameUndoRedoExt();
16043 }
16044
16045 static void GameRedo(int steps)
16046 {
16047   if (!CheckEngineSnapshotList())
16048     return;
16049
16050   LoadEngineSnapshot_Redo(steps);
16051
16052   GameUndoRedoExt();
16053 }
16054
16055 static void HandleGameButtonsExt(int id, int button)
16056 {
16057   static boolean game_undo_executed = FALSE;
16058   int steps = BUTTON_STEPSIZE(button);
16059   boolean handle_game_buttons =
16060     (game_status == GAME_MODE_PLAYING ||
16061      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16062
16063   if (!handle_game_buttons)
16064     return;
16065
16066   switch (id)
16067   {
16068     case GAME_CTRL_ID_STOP:
16069     case GAME_CTRL_ID_PANEL_STOP:
16070     case GAME_CTRL_ID_TOUCH_STOP:
16071       if (game_status == GAME_MODE_MAIN)
16072         break;
16073
16074       if (tape.playing)
16075         TapeStop();
16076       else
16077         RequestQuitGame(TRUE);
16078
16079       break;
16080
16081     case GAME_CTRL_ID_PAUSE:
16082     case GAME_CTRL_ID_PAUSE2:
16083     case GAME_CTRL_ID_PANEL_PAUSE:
16084     case GAME_CTRL_ID_TOUCH_PAUSE:
16085       if (network.enabled && game_status == GAME_MODE_PLAYING)
16086       {
16087         if (tape.pausing)
16088           SendToServer_ContinuePlaying();
16089         else
16090           SendToServer_PausePlaying();
16091       }
16092       else
16093         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16094
16095       game_undo_executed = FALSE;
16096
16097       break;
16098
16099     case GAME_CTRL_ID_PLAY:
16100     case GAME_CTRL_ID_PANEL_PLAY:
16101       if (game_status == GAME_MODE_MAIN)
16102       {
16103         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16104       }
16105       else if (tape.pausing)
16106       {
16107         if (network.enabled)
16108           SendToServer_ContinuePlaying();
16109         else
16110           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16111       }
16112       break;
16113
16114     case GAME_CTRL_ID_UNDO:
16115       // Important: When using "save snapshot when collecting an item" mode,
16116       // load last (current) snapshot for first "undo" after pressing "pause"
16117       // (else the last-but-one snapshot would be loaded, because the snapshot
16118       // pointer already points to the last snapshot when pressing "pause",
16119       // which is fine for "every step/move" mode, but not for "every collect")
16120       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16121           !game_undo_executed)
16122         steps--;
16123
16124       game_undo_executed = TRUE;
16125
16126       GameUndo(steps);
16127       break;
16128
16129     case GAME_CTRL_ID_REDO:
16130       GameRedo(steps);
16131       break;
16132
16133     case GAME_CTRL_ID_SAVE:
16134       TapeQuickSave();
16135       break;
16136
16137     case GAME_CTRL_ID_LOAD:
16138       TapeQuickLoad();
16139       break;
16140
16141     case SOUND_CTRL_ID_MUSIC:
16142     case SOUND_CTRL_ID_PANEL_MUSIC:
16143       if (setup.sound_music)
16144       { 
16145         setup.sound_music = FALSE;
16146
16147         FadeMusic();
16148       }
16149       else if (audio.music_available)
16150       { 
16151         setup.sound = setup.sound_music = TRUE;
16152
16153         SetAudioMode(setup.sound);
16154
16155         if (game_status == GAME_MODE_PLAYING)
16156           PlayLevelMusic();
16157       }
16158
16159       RedrawSoundButtonGadget(id);
16160
16161       break;
16162
16163     case SOUND_CTRL_ID_LOOPS:
16164     case SOUND_CTRL_ID_PANEL_LOOPS:
16165       if (setup.sound_loops)
16166         setup.sound_loops = FALSE;
16167       else if (audio.loops_available)
16168       {
16169         setup.sound = setup.sound_loops = TRUE;
16170
16171         SetAudioMode(setup.sound);
16172       }
16173
16174       RedrawSoundButtonGadget(id);
16175
16176       break;
16177
16178     case SOUND_CTRL_ID_SIMPLE:
16179     case SOUND_CTRL_ID_PANEL_SIMPLE:
16180       if (setup.sound_simple)
16181         setup.sound_simple = FALSE;
16182       else if (audio.sound_available)
16183       {
16184         setup.sound = setup.sound_simple = TRUE;
16185
16186         SetAudioMode(setup.sound);
16187       }
16188
16189       RedrawSoundButtonGadget(id);
16190
16191       break;
16192
16193     default:
16194       break;
16195   }
16196 }
16197
16198 static void HandleGameButtons(struct GadgetInfo *gi)
16199 {
16200   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16201 }
16202
16203 void HandleSoundButtonKeys(Key key)
16204 {
16205   if (key == setup.shortcut.sound_simple)
16206     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16207   else if (key == setup.shortcut.sound_loops)
16208     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16209   else if (key == setup.shortcut.sound_music)
16210     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16211 }