c926738df7c2a3c7f724a83c76d817a14bce78d3
[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(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2151       Error(ERR_EXIT, "this should not happen -- please debug");
2152     }
2153
2154     // force update of game controls after initialization
2155     gpc->value = gpc->last_value = -1;
2156     gpc->frame = gpc->last_frame = -1;
2157     gpc->gfx_frame = -1;
2158
2159     // determine panel value width for later calculation of alignment
2160     if (type == TYPE_INTEGER || type == TYPE_STRING)
2161     {
2162       pos->width = pos->size * getFontWidth(pos->font);
2163       pos->height = getFontHeight(pos->font);
2164     }
2165     else if (type == TYPE_ELEMENT)
2166     {
2167       pos->width = pos->size;
2168       pos->height = pos->size;
2169     }
2170
2171     // fill structure for game panel draw order
2172     gpo->nr = gpc->nr;
2173     gpo->sort_priority = pos->sort_priority;
2174   }
2175
2176   // sort game panel controls according to sort_priority and control number
2177   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2178         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2179 }
2180
2181 static void UpdatePlayfieldElementCount(void)
2182 {
2183   boolean use_element_count = FALSE;
2184   int i, j, x, y;
2185
2186   // first check if it is needed at all to calculate playfield element count
2187   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2188     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2189       use_element_count = TRUE;
2190
2191   if (!use_element_count)
2192     return;
2193
2194   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2195     element_info[i].element_count = 0;
2196
2197   SCAN_PLAYFIELD(x, y)
2198   {
2199     element_info[Tile[x][y]].element_count++;
2200   }
2201
2202   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2203     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2204       if (IS_IN_GROUP(j, i))
2205         element_info[EL_GROUP_START + i].element_count +=
2206           element_info[j].element_count;
2207 }
2208
2209 static void UpdateGameControlValues(void)
2210 {
2211   int i, k;
2212   int time = (game.LevelSolved ?
2213               game.LevelSolved_CountingTime :
2214               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2215               game_em.lev->time :
2216               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2217               game_sp.time_played :
2218               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2219               game_mm.energy_left :
2220               game.no_time_limit ? TimePlayed : TimeLeft);
2221   int score = (game.LevelSolved ?
2222                game.LevelSolved_CountingScore :
2223                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2224                game_em.lev->score :
2225                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2226                game_sp.score :
2227                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2228                game_mm.score :
2229                game.score);
2230   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2231               game_em.lev->gems_needed :
2232               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2233               game_sp.infotrons_still_needed :
2234               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2235               game_mm.kettles_still_needed :
2236               game.gems_still_needed);
2237   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2238                      game_em.lev->gems_needed > 0 :
2239                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2240                      game_sp.infotrons_still_needed > 0 :
2241                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2242                      game_mm.kettles_still_needed > 0 ||
2243                      game_mm.lights_still_needed > 0 :
2244                      game.gems_still_needed > 0 ||
2245                      game.sokoban_fields_still_needed > 0 ||
2246                      game.sokoban_objects_still_needed > 0 ||
2247                      game.lights_still_needed > 0);
2248   int health = (game.LevelSolved ?
2249                 game.LevelSolved_CountingHealth :
2250                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2251                 MM_HEALTH(game_mm.laser_overload_value) :
2252                 game.health);
2253
2254   UpdatePlayfieldElementCount();
2255
2256   // update game panel control values
2257
2258   // used instead of "level_nr" (for network games)
2259   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2260   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2261
2262   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2263   for (i = 0; i < MAX_NUM_KEYS; i++)
2264     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2265   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2266   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2267
2268   if (game.centered_player_nr == -1)
2269   {
2270     for (i = 0; i < MAX_PLAYERS; i++)
2271     {
2272       // only one player in Supaplex game engine
2273       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2274         break;
2275
2276       for (k = 0; k < MAX_NUM_KEYS; k++)
2277       {
2278         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2279         {
2280           if (game_em.ply[i]->keys & (1 << k))
2281             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2282               get_key_element_from_nr(k);
2283         }
2284         else if (stored_player[i].key[k])
2285           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2286             get_key_element_from_nr(k);
2287       }
2288
2289       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2290         getPlayerInventorySize(i);
2291
2292       if (stored_player[i].num_white_keys > 0)
2293         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2294           EL_DC_KEY_WHITE;
2295
2296       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2297         stored_player[i].num_white_keys;
2298     }
2299   }
2300   else
2301   {
2302     int player_nr = game.centered_player_nr;
2303
2304     for (k = 0; k < MAX_NUM_KEYS; k++)
2305     {
2306       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2307       {
2308         if (game_em.ply[player_nr]->keys & (1 << k))
2309           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2310             get_key_element_from_nr(k);
2311       }
2312       else if (stored_player[player_nr].key[k])
2313         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2314           get_key_element_from_nr(k);
2315     }
2316
2317     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2318       getPlayerInventorySize(player_nr);
2319
2320     if (stored_player[player_nr].num_white_keys > 0)
2321       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2322
2323     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2324       stored_player[player_nr].num_white_keys;
2325   }
2326
2327   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2328   {
2329     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2330       get_inventory_element_from_pos(local_player, i);
2331     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2332       get_inventory_element_from_pos(local_player, -i - 1);
2333   }
2334
2335   game_panel_controls[GAME_PANEL_SCORE].value = score;
2336   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2337
2338   game_panel_controls[GAME_PANEL_TIME].value = time;
2339
2340   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2341   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2342   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2343
2344   if (level.time == 0)
2345     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2346   else
2347     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2348
2349   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2350   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2351
2352   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2353
2354   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2355     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2356      EL_EMPTY);
2357   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2358     local_player->shield_normal_time_left;
2359   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2360     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2361      EL_EMPTY);
2362   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2363     local_player->shield_deadly_time_left;
2364
2365   game_panel_controls[GAME_PANEL_EXIT].value =
2366     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2367
2368   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2369     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2370   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2371     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2372      EL_EMC_MAGIC_BALL_SWITCH);
2373
2374   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2375     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2376   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2377     game.light_time_left;
2378
2379   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2380     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2381   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2382     game.timegate_time_left;
2383
2384   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2385     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2386
2387   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2388     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2389   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2390     game.lenses_time_left;
2391
2392   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2393     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2394   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2395     game.magnify_time_left;
2396
2397   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2398     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2399      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2400      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2401      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2402      EL_BALLOON_SWITCH_NONE);
2403
2404   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2405     local_player->dynabomb_count;
2406   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2407     local_player->dynabomb_size;
2408   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2409     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2410
2411   game_panel_controls[GAME_PANEL_PENGUINS].value =
2412     game.friends_still_needed;
2413
2414   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2415     game.sokoban_objects_still_needed;
2416   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2417     game.sokoban_fields_still_needed;
2418
2419   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2420     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2421
2422   for (i = 0; i < NUM_BELTS; i++)
2423   {
2424     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2425       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2426        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2427     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2428       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2429   }
2430
2431   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2432     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2433   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2434     game.magic_wall_time_left;
2435
2436   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2437     local_player->gravity;
2438
2439   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2440     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2441
2442   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2443     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2444       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2445        game.panel.element[i].id : EL_UNDEFINED);
2446
2447   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2448     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2449       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2450        element_info[game.panel.element_count[i].id].element_count : 0);
2451
2452   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2453     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2454       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2455        element_info[game.panel.ce_score[i].id].collect_score : 0);
2456
2457   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2458     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2459       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2460        element_info[game.panel.ce_score_element[i].id].collect_score :
2461        EL_UNDEFINED);
2462
2463   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2464   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2465   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2466
2467   // update game panel control frames
2468
2469   for (i = 0; game_panel_controls[i].nr != -1; i++)
2470   {
2471     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2472
2473     if (gpc->type == TYPE_ELEMENT)
2474     {
2475       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2476       {
2477         int last_anim_random_frame = gfx.anim_random_frame;
2478         int element = gpc->value;
2479         int graphic = el2panelimg(element);
2480
2481         if (gpc->value != gpc->last_value)
2482         {
2483           gpc->gfx_frame = 0;
2484           gpc->gfx_random = INIT_GFX_RANDOM();
2485         }
2486         else
2487         {
2488           gpc->gfx_frame++;
2489
2490           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2491               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2492             gpc->gfx_random = INIT_GFX_RANDOM();
2493         }
2494
2495         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2496           gfx.anim_random_frame = gpc->gfx_random;
2497
2498         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2499           gpc->gfx_frame = element_info[element].collect_score;
2500
2501         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2502                                               gpc->gfx_frame);
2503
2504         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2505           gfx.anim_random_frame = last_anim_random_frame;
2506       }
2507     }
2508     else if (gpc->type == TYPE_GRAPHIC)
2509     {
2510       if (gpc->graphic != IMG_UNDEFINED)
2511       {
2512         int last_anim_random_frame = gfx.anim_random_frame;
2513         int graphic = gpc->graphic;
2514
2515         if (gpc->value != gpc->last_value)
2516         {
2517           gpc->gfx_frame = 0;
2518           gpc->gfx_random = INIT_GFX_RANDOM();
2519         }
2520         else
2521         {
2522           gpc->gfx_frame++;
2523
2524           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2525               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2526             gpc->gfx_random = INIT_GFX_RANDOM();
2527         }
2528
2529         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2530           gfx.anim_random_frame = gpc->gfx_random;
2531
2532         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2533
2534         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2535           gfx.anim_random_frame = last_anim_random_frame;
2536       }
2537     }
2538   }
2539 }
2540
2541 static void DisplayGameControlValues(void)
2542 {
2543   boolean redraw_panel = FALSE;
2544   int i;
2545
2546   for (i = 0; game_panel_controls[i].nr != -1; i++)
2547   {
2548     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2549
2550     if (PANEL_DEACTIVATED(gpc->pos))
2551       continue;
2552
2553     if (gpc->value == gpc->last_value &&
2554         gpc->frame == gpc->last_frame)
2555       continue;
2556
2557     redraw_panel = TRUE;
2558   }
2559
2560   if (!redraw_panel)
2561     return;
2562
2563   // copy default game door content to main double buffer
2564
2565   // !!! CHECK AGAIN !!!
2566   SetPanelBackground();
2567   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2568   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2569
2570   // redraw game control buttons
2571   RedrawGameButtons();
2572
2573   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2574
2575   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2576   {
2577     int nr = game_panel_order[i].nr;
2578     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2579     struct TextPosInfo *pos = gpc->pos;
2580     int type = gpc->type;
2581     int value = gpc->value;
2582     int frame = gpc->frame;
2583     int size = pos->size;
2584     int font = pos->font;
2585     boolean draw_masked = pos->draw_masked;
2586     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2587
2588     if (PANEL_DEACTIVATED(pos))
2589       continue;
2590
2591     gpc->last_value = value;
2592     gpc->last_frame = frame;
2593
2594     if (type == TYPE_INTEGER)
2595     {
2596       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2597           nr == GAME_PANEL_TIME)
2598       {
2599         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2600
2601         if (use_dynamic_size)           // use dynamic number of digits
2602         {
2603           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2604           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2605           int size2 = size1 + 1;
2606           int font1 = pos->font;
2607           int font2 = pos->font_alt;
2608
2609           size = (value < value_change ? size1 : size2);
2610           font = (value < value_change ? font1 : font2);
2611         }
2612       }
2613
2614       // correct text size if "digits" is zero or less
2615       if (size <= 0)
2616         size = strlen(int2str(value, size));
2617
2618       // dynamically correct text alignment
2619       pos->width = size * getFontWidth(font);
2620
2621       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2622                   int2str(value, size), font, mask_mode);
2623     }
2624     else if (type == TYPE_ELEMENT)
2625     {
2626       int element, graphic;
2627       Bitmap *src_bitmap;
2628       int src_x, src_y;
2629       int width, height;
2630       int dst_x = PANEL_XPOS(pos);
2631       int dst_y = PANEL_YPOS(pos);
2632
2633       if (value != EL_UNDEFINED && value != EL_EMPTY)
2634       {
2635         element = value;
2636         graphic = el2panelimg(value);
2637
2638         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2639
2640         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2641           size = TILESIZE;
2642
2643         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2644                               &src_x, &src_y);
2645
2646         width  = graphic_info[graphic].width  * size / TILESIZE;
2647         height = graphic_info[graphic].height * size / TILESIZE;
2648
2649         if (draw_masked)
2650           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2651                            dst_x, dst_y);
2652         else
2653           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2654                      dst_x, dst_y);
2655       }
2656     }
2657     else if (type == TYPE_GRAPHIC)
2658     {
2659       int graphic        = gpc->graphic;
2660       int graphic_active = gpc->graphic_active;
2661       Bitmap *src_bitmap;
2662       int src_x, src_y;
2663       int width, height;
2664       int dst_x = PANEL_XPOS(pos);
2665       int dst_y = PANEL_YPOS(pos);
2666       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2667                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2668
2669       if (graphic != IMG_UNDEFINED && !skip)
2670       {
2671         if (pos->style == STYLE_REVERSE)
2672           value = 100 - value;
2673
2674         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2675
2676         if (pos->direction & MV_HORIZONTAL)
2677         {
2678           width  = graphic_info[graphic_active].width * value / 100;
2679           height = graphic_info[graphic_active].height;
2680
2681           if (pos->direction == MV_LEFT)
2682           {
2683             src_x += graphic_info[graphic_active].width - width;
2684             dst_x += graphic_info[graphic_active].width - width;
2685           }
2686         }
2687         else
2688         {
2689           width  = graphic_info[graphic_active].width;
2690           height = graphic_info[graphic_active].height * value / 100;
2691
2692           if (pos->direction == MV_UP)
2693           {
2694             src_y += graphic_info[graphic_active].height - height;
2695             dst_y += graphic_info[graphic_active].height - height;
2696           }
2697         }
2698
2699         if (draw_masked)
2700           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2701                            dst_x, dst_y);
2702         else
2703           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2704                      dst_x, dst_y);
2705
2706         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2707
2708         if (pos->direction & MV_HORIZONTAL)
2709         {
2710           if (pos->direction == MV_RIGHT)
2711           {
2712             src_x += width;
2713             dst_x += width;
2714           }
2715           else
2716           {
2717             dst_x = PANEL_XPOS(pos);
2718           }
2719
2720           width = graphic_info[graphic].width - width;
2721         }
2722         else
2723         {
2724           if (pos->direction == MV_DOWN)
2725           {
2726             src_y += height;
2727             dst_y += height;
2728           }
2729           else
2730           {
2731             dst_y = PANEL_YPOS(pos);
2732           }
2733
2734           height = graphic_info[graphic].height - height;
2735         }
2736
2737         if (draw_masked)
2738           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2739                            dst_x, dst_y);
2740         else
2741           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2742                      dst_x, dst_y);
2743       }
2744     }
2745     else if (type == TYPE_STRING)
2746     {
2747       boolean active = (value != 0);
2748       char *state_normal = "off";
2749       char *state_active = "on";
2750       char *state = (active ? state_active : state_normal);
2751       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2752                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2753                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2754                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2755
2756       if (nr == GAME_PANEL_GRAVITY_STATE)
2757       {
2758         int font1 = pos->font;          // (used for normal state)
2759         int font2 = pos->font_alt;      // (used for active state)
2760
2761         font = (active ? font2 : font1);
2762       }
2763
2764       if (s != NULL)
2765       {
2766         char *s_cut;
2767
2768         if (size <= 0)
2769         {
2770           // don't truncate output if "chars" is zero or less
2771           size = strlen(s);
2772
2773           // dynamically correct text alignment
2774           pos->width = size * getFontWidth(font);
2775         }
2776
2777         s_cut = getStringCopyN(s, size);
2778
2779         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2780                     s_cut, font, mask_mode);
2781
2782         free(s_cut);
2783       }
2784     }
2785
2786     redraw_mask |= REDRAW_DOOR_1;
2787   }
2788
2789   SetGameStatus(GAME_MODE_PLAYING);
2790 }
2791
2792 void UpdateAndDisplayGameControlValues(void)
2793 {
2794   if (tape.deactivate_display)
2795     return;
2796
2797   UpdateGameControlValues();
2798   DisplayGameControlValues();
2799 }
2800
2801 #if 0
2802 static void UpdateGameDoorValues(void)
2803 {
2804   UpdateGameControlValues();
2805 }
2806 #endif
2807
2808 void DrawGameDoorValues(void)
2809 {
2810   DisplayGameControlValues();
2811 }
2812
2813
2814 // ============================================================================
2815 // InitGameEngine()
2816 // ----------------------------------------------------------------------------
2817 // initialize game engine due to level / tape version number
2818 // ============================================================================
2819
2820 static void InitGameEngine(void)
2821 {
2822   int i, j, k, l, x, y;
2823
2824   // set game engine from tape file when re-playing, else from level file
2825   game.engine_version = (tape.playing ? tape.engine_version :
2826                          level.game_version);
2827
2828   // set single or multi-player game mode (needed for re-playing tapes)
2829   game.team_mode = setup.team_mode;
2830
2831   if (tape.playing)
2832   {
2833     int num_players = 0;
2834
2835     for (i = 0; i < MAX_PLAYERS; i++)
2836       if (tape.player_participates[i])
2837         num_players++;
2838
2839     // multi-player tapes contain input data for more than one player
2840     game.team_mode = (num_players > 1);
2841   }
2842
2843 #if 0
2844   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2845         level.game_version);
2846   Debug("game:init:level", "          tape.file_version   == %06d",
2847         tape.file_version);
2848   Debug("game:init:level", "          tape.game_version   == %06d",
2849         tape.game_version);
2850   Debug("game:init:level", "          tape.engine_version == %06d",
2851         tape.engine_version);
2852   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2853         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2854 #endif
2855
2856   // --------------------------------------------------------------------------
2857   // set flags for bugs and changes according to active game engine version
2858   // --------------------------------------------------------------------------
2859
2860   /*
2861     Summary of bugfix:
2862     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2863
2864     Bug was introduced in version:
2865     2.0.1
2866
2867     Bug was fixed in version:
2868     4.2.0.0
2869
2870     Description:
2871     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2872     but the property "can fall" was missing, which caused some levels to be
2873     unsolvable. This was fixed in version 4.2.0.0.
2874
2875     Affected levels/tapes:
2876     An example for a tape that was fixed by this bugfix is tape 029 from the
2877     level set "rnd_sam_bateman".
2878     The wrong behaviour will still be used for all levels or tapes that were
2879     created/recorded with it. An example for this is tape 023 from the level
2880     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2881   */
2882
2883   boolean use_amoeba_dropping_cannot_fall_bug =
2884     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2885       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2886      (tape.playing &&
2887       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2888       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2889
2890   /*
2891     Summary of bugfix/change:
2892     Fixed move speed of elements entering or leaving magic wall.
2893
2894     Fixed/changed in version:
2895     2.0.1
2896
2897     Description:
2898     Before 2.0.1, move speed of elements entering or leaving magic wall was
2899     twice as fast as it is now.
2900     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2901
2902     Affected levels/tapes:
2903     The first condition is generally needed for all levels/tapes before version
2904     2.0.1, which might use the old behaviour before it was changed; known tapes
2905     that are affected: Tape 014 from the level set "rnd_conor_mancone".
2906     The second condition is an exception from the above case and is needed for
2907     the special case of tapes recorded with game (not engine!) version 2.0.1 or
2908     above, but before it was known that this change would break tapes like the
2909     above and was fixed in 4.2.0.0, so that the changed behaviour was active
2910     although the engine version while recording maybe was before 2.0.1. There
2911     are a lot of tapes that are affected by this exception, like tape 006 from
2912     the level set "rnd_conor_mancone".
2913   */
2914
2915   boolean use_old_move_stepsize_for_magic_wall =
2916     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
2917      !(tape.playing &&
2918        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2919        tape.game_version <  VERSION_IDENT(4,2,0,0)));
2920
2921   /*
2922     Summary of bugfix/change:
2923     Fixed handling for custom elements that change when pushed by the player.
2924
2925     Fixed/changed in version:
2926     3.1.0
2927
2928     Description:
2929     Before 3.1.0, custom elements that "change when pushing" changed directly
2930     after the player started pushing them (until then handled in "DigField()").
2931     Since 3.1.0, these custom elements are not changed until the "pushing"
2932     move of the element is finished (now handled in "ContinueMoving()").
2933
2934     Affected levels/tapes:
2935     The first condition is generally needed for all levels/tapes before version
2936     3.1.0, which might use the old behaviour before it was changed; known tapes
2937     that are affected are some tapes from the level set "Walpurgis Gardens" by
2938     Jamie Cullen.
2939     The second condition is an exception from the above case and is needed for
2940     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2941     above (including some development versions of 3.1.0), but before it was
2942     known that this change would break tapes like the above and was fixed in
2943     3.1.1, so that the changed behaviour was active although the engine version
2944     while recording maybe was before 3.1.0. There is at least one tape that is
2945     affected by this exception, which is the tape for the one-level set "Bug
2946     Machine" by Juergen Bonhagen.
2947   */
2948
2949   game.use_change_when_pushing_bug =
2950     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2951      !(tape.playing &&
2952        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2953        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2954
2955   /*
2956     Summary of bugfix/change:
2957     Fixed handling for blocking the field the player leaves when moving.
2958
2959     Fixed/changed in version:
2960     3.1.1
2961
2962     Description:
2963     Before 3.1.1, when "block last field when moving" was enabled, the field
2964     the player is leaving when moving was blocked for the time of the move,
2965     and was directly unblocked afterwards. This resulted in the last field
2966     being blocked for exactly one less than the number of frames of one player
2967     move. Additionally, even when blocking was disabled, the last field was
2968     blocked for exactly one frame.
2969     Since 3.1.1, due to changes in player movement handling, the last field
2970     is not blocked at all when blocking is disabled. When blocking is enabled,
2971     the last field is blocked for exactly the number of frames of one player
2972     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2973     last field is blocked for exactly one more than the number of frames of
2974     one player move.
2975
2976     Affected levels/tapes:
2977     (!!! yet to be determined -- probably many !!!)
2978   */
2979
2980   game.use_block_last_field_bug =
2981     (game.engine_version < VERSION_IDENT(3,1,1,0));
2982
2983   /* various special flags and settings for native Emerald Mine game engine */
2984
2985   game_em.use_single_button =
2986     (game.engine_version > VERSION_IDENT(4,0,0,2));
2987
2988   game_em.use_snap_key_bug =
2989     (game.engine_version < VERSION_IDENT(4,0,1,0));
2990
2991   game_em.use_random_bug =
2992     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
2993
2994   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
2995
2996   game_em.use_old_explosions            = use_old_em_engine;
2997   game_em.use_old_android               = use_old_em_engine;
2998   game_em.use_old_push_elements         = use_old_em_engine;
2999   game_em.use_old_push_into_acid        = use_old_em_engine;
3000
3001   game_em.use_wrap_around               = !use_old_em_engine;
3002
3003   // --------------------------------------------------------------------------
3004
3005   // set maximal allowed number of custom element changes per game frame
3006   game.max_num_changes_per_frame = 1;
3007
3008   // default scan direction: scan playfield from top/left to bottom/right
3009   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3010
3011   // dynamically adjust element properties according to game engine version
3012   InitElementPropertiesEngine(game.engine_version);
3013
3014   // ---------- initialize special element properties -------------------------
3015
3016   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3017   if (use_amoeba_dropping_cannot_fall_bug)
3018     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3019
3020   // ---------- initialize player's initial move delay ------------------------
3021
3022   // dynamically adjust player properties according to level information
3023   for (i = 0; i < MAX_PLAYERS; i++)
3024     game.initial_move_delay_value[i] =
3025       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3026
3027   // dynamically adjust player properties according to game engine version
3028   for (i = 0; i < MAX_PLAYERS; i++)
3029     game.initial_move_delay[i] =
3030       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3031        game.initial_move_delay_value[i] : 0);
3032
3033   // ---------- initialize player's initial push delay ------------------------
3034
3035   // dynamically adjust player properties according to game engine version
3036   game.initial_push_delay_value =
3037     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3038
3039   // ---------- initialize changing elements ----------------------------------
3040
3041   // initialize changing elements information
3042   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3043   {
3044     struct ElementInfo *ei = &element_info[i];
3045
3046     // this pointer might have been changed in the level editor
3047     ei->change = &ei->change_page[0];
3048
3049     if (!IS_CUSTOM_ELEMENT(i))
3050     {
3051       ei->change->target_element = EL_EMPTY_SPACE;
3052       ei->change->delay_fixed = 0;
3053       ei->change->delay_random = 0;
3054       ei->change->delay_frames = 1;
3055     }
3056
3057     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3058     {
3059       ei->has_change_event[j] = FALSE;
3060
3061       ei->event_page_nr[j] = 0;
3062       ei->event_page[j] = &ei->change_page[0];
3063     }
3064   }
3065
3066   // add changing elements from pre-defined list
3067   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3068   {
3069     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3070     struct ElementInfo *ei = &element_info[ch_delay->element];
3071
3072     ei->change->target_element       = ch_delay->target_element;
3073     ei->change->delay_fixed          = ch_delay->change_delay;
3074
3075     ei->change->pre_change_function  = ch_delay->pre_change_function;
3076     ei->change->change_function      = ch_delay->change_function;
3077     ei->change->post_change_function = ch_delay->post_change_function;
3078
3079     ei->change->can_change = TRUE;
3080     ei->change->can_change_or_has_action = TRUE;
3081
3082     ei->has_change_event[CE_DELAY] = TRUE;
3083
3084     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3085     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3086   }
3087
3088   // ---------- initialize internal run-time variables ------------------------
3089
3090   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3091   {
3092     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3093
3094     for (j = 0; j < ei->num_change_pages; j++)
3095     {
3096       ei->change_page[j].can_change_or_has_action =
3097         (ei->change_page[j].can_change |
3098          ei->change_page[j].has_action);
3099     }
3100   }
3101
3102   // add change events from custom element configuration
3103   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3104   {
3105     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3106
3107     for (j = 0; j < ei->num_change_pages; j++)
3108     {
3109       if (!ei->change_page[j].can_change_or_has_action)
3110         continue;
3111
3112       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3113       {
3114         // only add event page for the first page found with this event
3115         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3116         {
3117           ei->has_change_event[k] = TRUE;
3118
3119           ei->event_page_nr[k] = j;
3120           ei->event_page[k] = &ei->change_page[j];
3121         }
3122       }
3123     }
3124   }
3125
3126   // ---------- initialize reference elements in change conditions ------------
3127
3128   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3129   {
3130     int element = EL_CUSTOM_START + i;
3131     struct ElementInfo *ei = &element_info[element];
3132
3133     for (j = 0; j < ei->num_change_pages; j++)
3134     {
3135       int trigger_element = ei->change_page[j].initial_trigger_element;
3136
3137       if (trigger_element >= EL_PREV_CE_8 &&
3138           trigger_element <= EL_NEXT_CE_8)
3139         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3140
3141       ei->change_page[j].trigger_element = trigger_element;
3142     }
3143   }
3144
3145   // ---------- initialize run-time trigger player and element ----------------
3146
3147   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3148   {
3149     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3150
3151     for (j = 0; j < ei->num_change_pages; j++)
3152     {
3153       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3154       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3155       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3156       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3157       ei->change_page[j].actual_trigger_ce_value = 0;
3158       ei->change_page[j].actual_trigger_ce_score = 0;
3159     }
3160   }
3161
3162   // ---------- initialize trigger events -------------------------------------
3163
3164   // initialize trigger events information
3165   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3166     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3167       trigger_events[i][j] = FALSE;
3168
3169   // add trigger events from element change event properties
3170   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3171   {
3172     struct ElementInfo *ei = &element_info[i];
3173
3174     for (j = 0; j < ei->num_change_pages; j++)
3175     {
3176       if (!ei->change_page[j].can_change_or_has_action)
3177         continue;
3178
3179       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3180       {
3181         int trigger_element = ei->change_page[j].trigger_element;
3182
3183         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3184         {
3185           if (ei->change_page[j].has_event[k])
3186           {
3187             if (IS_GROUP_ELEMENT(trigger_element))
3188             {
3189               struct ElementGroupInfo *group =
3190                 element_info[trigger_element].group;
3191
3192               for (l = 0; l < group->num_elements_resolved; l++)
3193                 trigger_events[group->element_resolved[l]][k] = TRUE;
3194             }
3195             else if (trigger_element == EL_ANY_ELEMENT)
3196               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3197                 trigger_events[l][k] = TRUE;
3198             else
3199               trigger_events[trigger_element][k] = TRUE;
3200           }
3201         }
3202       }
3203     }
3204   }
3205
3206   // ---------- initialize push delay -----------------------------------------
3207
3208   // initialize push delay values to default
3209   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3210   {
3211     if (!IS_CUSTOM_ELEMENT(i))
3212     {
3213       // set default push delay values (corrected since version 3.0.7-1)
3214       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3215       {
3216         element_info[i].push_delay_fixed = 2;
3217         element_info[i].push_delay_random = 8;
3218       }
3219       else
3220       {
3221         element_info[i].push_delay_fixed = 8;
3222         element_info[i].push_delay_random = 8;
3223       }
3224     }
3225   }
3226
3227   // set push delay value for certain elements from pre-defined list
3228   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3229   {
3230     int e = push_delay_list[i].element;
3231
3232     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3233     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3234   }
3235
3236   // set push delay value for Supaplex elements for newer engine versions
3237   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3238   {
3239     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3240     {
3241       if (IS_SP_ELEMENT(i))
3242       {
3243         // set SP push delay to just enough to push under a falling zonk
3244         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3245
3246         element_info[i].push_delay_fixed  = delay;
3247         element_info[i].push_delay_random = 0;
3248       }
3249     }
3250   }
3251
3252   // ---------- initialize move stepsize --------------------------------------
3253
3254   // initialize move stepsize values to default
3255   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3256     if (!IS_CUSTOM_ELEMENT(i))
3257       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3258
3259   // set move stepsize value for certain elements from pre-defined list
3260   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3261   {
3262     int e = move_stepsize_list[i].element;
3263
3264     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3265
3266     // set move stepsize value for certain elements for older engine versions
3267     if (use_old_move_stepsize_for_magic_wall)
3268     {
3269       if (e == EL_MAGIC_WALL_FILLING ||
3270           e == EL_MAGIC_WALL_EMPTYING ||
3271           e == EL_BD_MAGIC_WALL_FILLING ||
3272           e == EL_BD_MAGIC_WALL_EMPTYING)
3273         element_info[e].move_stepsize *= 2;
3274     }
3275   }
3276
3277   // ---------- initialize collect score --------------------------------------
3278
3279   // initialize collect score values for custom elements from initial value
3280   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3281     if (IS_CUSTOM_ELEMENT(i))
3282       element_info[i].collect_score = element_info[i].collect_score_initial;
3283
3284   // ---------- initialize collect count --------------------------------------
3285
3286   // initialize collect count values for non-custom elements
3287   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3288     if (!IS_CUSTOM_ELEMENT(i))
3289       element_info[i].collect_count_initial = 0;
3290
3291   // add collect count values for all elements from pre-defined list
3292   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3293     element_info[collect_count_list[i].element].collect_count_initial =
3294       collect_count_list[i].count;
3295
3296   // ---------- initialize access direction -----------------------------------
3297
3298   // initialize access direction values to default (access from every side)
3299   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3300     if (!IS_CUSTOM_ELEMENT(i))
3301       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3302
3303   // set access direction value for certain elements from pre-defined list
3304   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3305     element_info[access_direction_list[i].element].access_direction =
3306       access_direction_list[i].direction;
3307
3308   // ---------- initialize explosion content ----------------------------------
3309   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3310   {
3311     if (IS_CUSTOM_ELEMENT(i))
3312       continue;
3313
3314     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3315     {
3316       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3317
3318       element_info[i].content.e[x][y] =
3319         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3320          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3321          i == EL_PLAYER_3 ? EL_EMERALD :
3322          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3323          i == EL_MOLE ? EL_EMERALD_RED :
3324          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3325          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3326          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3327          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3328          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3329          i == EL_WALL_EMERALD ? EL_EMERALD :
3330          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3331          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3332          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3333          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3334          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3335          i == EL_WALL_PEARL ? EL_PEARL :
3336          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3337          EL_EMPTY);
3338     }
3339   }
3340
3341   // ---------- initialize recursion detection --------------------------------
3342   recursion_loop_depth = 0;
3343   recursion_loop_detected = FALSE;
3344   recursion_loop_element = EL_UNDEFINED;
3345
3346   // ---------- initialize graphics engine ------------------------------------
3347   game.scroll_delay_value =
3348     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3349      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3350      !setup.forced_scroll_delay           ? 0 :
3351      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3352   game.scroll_delay_value =
3353     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3354
3355   // ---------- initialize game engine snapshots ------------------------------
3356   for (i = 0; i < MAX_PLAYERS; i++)
3357     game.snapshot.last_action[i] = 0;
3358   game.snapshot.changed_action = FALSE;
3359   game.snapshot.collected_item = FALSE;
3360   game.snapshot.mode =
3361     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3362      SNAPSHOT_MODE_EVERY_STEP :
3363      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3364      SNAPSHOT_MODE_EVERY_MOVE :
3365      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3366      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3367   game.snapshot.save_snapshot = FALSE;
3368
3369   // ---------- initialize level time for Supaplex engine ---------------------
3370   // Supaplex levels with time limit currently unsupported -- should be added
3371   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3372     level.time = 0;
3373
3374   // ---------- initialize flags for handling game actions --------------------
3375
3376   // set flags for game actions to default values
3377   game.use_key_actions = TRUE;
3378   game.use_mouse_actions = FALSE;
3379
3380   // when using Mirror Magic game engine, handle mouse events only
3381   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3382   {
3383     game.use_key_actions = FALSE;
3384     game.use_mouse_actions = TRUE;
3385   }
3386
3387   // check for custom elements with mouse click events
3388   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3389   {
3390     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3391     {
3392       int element = EL_CUSTOM_START + i;
3393
3394       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3395           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3396           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3397           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3398         game.use_mouse_actions = TRUE;
3399     }
3400   }
3401 }
3402
3403 static int get_num_special_action(int element, int action_first,
3404                                   int action_last)
3405 {
3406   int num_special_action = 0;
3407   int i, j;
3408
3409   for (i = action_first; i <= action_last; i++)
3410   {
3411     boolean found = FALSE;
3412
3413     for (j = 0; j < NUM_DIRECTIONS; j++)
3414       if (el_act_dir2img(element, i, j) !=
3415           el_act_dir2img(element, ACTION_DEFAULT, j))
3416         found = TRUE;
3417
3418     if (found)
3419       num_special_action++;
3420     else
3421       break;
3422   }
3423
3424   return num_special_action;
3425 }
3426
3427
3428 // ============================================================================
3429 // InitGame()
3430 // ----------------------------------------------------------------------------
3431 // initialize and start new game
3432 // ============================================================================
3433
3434 #if DEBUG_INIT_PLAYER
3435 static void DebugPrintPlayerStatus(char *message)
3436 {
3437   int i;
3438
3439   if (!options.debug)
3440     return;
3441
3442   Debug("game:init:player", "%s:", message);
3443
3444   for (i = 0; i < MAX_PLAYERS; i++)
3445   {
3446     struct PlayerInfo *player = &stored_player[i];
3447
3448     Debug("game:init:player",
3449           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3450           i + 1,
3451           player->present,
3452           player->connected,
3453           player->connected_locally,
3454           player->connected_network,
3455           player->active,
3456           (local_player == player ? " (local player)" : ""));
3457   }
3458 }
3459 #endif
3460
3461 void InitGame(void)
3462 {
3463   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3464   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3465   int fade_mask = REDRAW_FIELD;
3466
3467   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3468   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3469   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3470   int initial_move_dir = MV_DOWN;
3471   int i, j, x, y;
3472
3473   // required here to update video display before fading (FIX THIS)
3474   DrawMaskedBorder(REDRAW_DOOR_2);
3475
3476   if (!game.restart_level)
3477     CloseDoor(DOOR_CLOSE_1);
3478
3479   SetGameStatus(GAME_MODE_PLAYING);
3480
3481   if (level_editor_test_game)
3482     FadeSkipNextFadeOut();
3483   else
3484     FadeSetEnterScreen();
3485
3486   if (CheckFadeAll())
3487     fade_mask = REDRAW_ALL;
3488
3489   FadeLevelSoundsAndMusic();
3490
3491   ExpireSoundLoops(TRUE);
3492
3493   FadeOut(fade_mask);
3494
3495   if (level_editor_test_game)
3496     FadeSkipNextFadeIn();
3497
3498   // needed if different viewport properties defined for playing
3499   ChangeViewportPropertiesIfNeeded();
3500
3501   ClearField();
3502
3503   DrawCompleteVideoDisplay();
3504
3505   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3506
3507   InitGameEngine();
3508   InitGameControlValues();
3509
3510   // initialize tape actions from game when recording tape
3511   if (tape.recording)
3512   {
3513     tape.use_key_actions   = game.use_key_actions;
3514     tape.use_mouse_actions = game.use_mouse_actions;
3515   }
3516
3517   // don't play tapes over network
3518   network_playing = (network.enabled && !tape.playing);
3519
3520   for (i = 0; i < MAX_PLAYERS; i++)
3521   {
3522     struct PlayerInfo *player = &stored_player[i];
3523
3524     player->index_nr = i;
3525     player->index_bit = (1 << i);
3526     player->element_nr = EL_PLAYER_1 + i;
3527
3528     player->present = FALSE;
3529     player->active = FALSE;
3530     player->mapped = FALSE;
3531
3532     player->killed = FALSE;
3533     player->reanimated = FALSE;
3534     player->buried = FALSE;
3535
3536     player->action = 0;
3537     player->effective_action = 0;
3538     player->programmed_action = 0;
3539     player->snap_action = 0;
3540
3541     player->mouse_action.lx = 0;
3542     player->mouse_action.ly = 0;
3543     player->mouse_action.button = 0;
3544     player->mouse_action.button_hint = 0;
3545
3546     player->effective_mouse_action.lx = 0;
3547     player->effective_mouse_action.ly = 0;
3548     player->effective_mouse_action.button = 0;
3549     player->effective_mouse_action.button_hint = 0;
3550
3551     for (j = 0; j < MAX_NUM_KEYS; j++)
3552       player->key[j] = FALSE;
3553
3554     player->num_white_keys = 0;
3555
3556     player->dynabomb_count = 0;
3557     player->dynabomb_size = 1;
3558     player->dynabombs_left = 0;
3559     player->dynabomb_xl = FALSE;
3560
3561     player->MovDir = initial_move_dir;
3562     player->MovPos = 0;
3563     player->GfxPos = 0;
3564     player->GfxDir = initial_move_dir;
3565     player->GfxAction = ACTION_DEFAULT;
3566     player->Frame = 0;
3567     player->StepFrame = 0;
3568
3569     player->initial_element = player->element_nr;
3570     player->artwork_element =
3571       (level.use_artwork_element[i] ? level.artwork_element[i] :
3572        player->element_nr);
3573     player->use_murphy = FALSE;
3574
3575     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3576     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3577
3578     player->gravity = level.initial_player_gravity[i];
3579
3580     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3581
3582     player->actual_frame_counter = 0;
3583
3584     player->step_counter = 0;
3585
3586     player->last_move_dir = initial_move_dir;
3587
3588     player->is_active = FALSE;
3589
3590     player->is_waiting = FALSE;
3591     player->is_moving = FALSE;
3592     player->is_auto_moving = FALSE;
3593     player->is_digging = FALSE;
3594     player->is_snapping = FALSE;
3595     player->is_collecting = FALSE;
3596     player->is_pushing = FALSE;
3597     player->is_switching = FALSE;
3598     player->is_dropping = FALSE;
3599     player->is_dropping_pressed = FALSE;
3600
3601     player->is_bored = FALSE;
3602     player->is_sleeping = FALSE;
3603
3604     player->was_waiting = TRUE;
3605     player->was_moving = FALSE;
3606     player->was_snapping = FALSE;
3607     player->was_dropping = FALSE;
3608
3609     player->force_dropping = FALSE;
3610
3611     player->frame_counter_bored = -1;
3612     player->frame_counter_sleeping = -1;
3613
3614     player->anim_delay_counter = 0;
3615     player->post_delay_counter = 0;
3616
3617     player->dir_waiting = initial_move_dir;
3618     player->action_waiting = ACTION_DEFAULT;
3619     player->last_action_waiting = ACTION_DEFAULT;
3620     player->special_action_bored = ACTION_DEFAULT;
3621     player->special_action_sleeping = ACTION_DEFAULT;
3622
3623     player->switch_x = -1;
3624     player->switch_y = -1;
3625
3626     player->drop_x = -1;
3627     player->drop_y = -1;
3628
3629     player->show_envelope = 0;
3630
3631     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3632
3633     player->push_delay       = -1;      // initialized when pushing starts
3634     player->push_delay_value = game.initial_push_delay_value;
3635
3636     player->drop_delay = 0;
3637     player->drop_pressed_delay = 0;
3638
3639     player->last_jx = -1;
3640     player->last_jy = -1;
3641     player->jx = -1;
3642     player->jy = -1;
3643
3644     player->shield_normal_time_left = 0;
3645     player->shield_deadly_time_left = 0;
3646
3647     player->inventory_infinite_element = EL_UNDEFINED;
3648     player->inventory_size = 0;
3649
3650     if (level.use_initial_inventory[i])
3651     {
3652       for (j = 0; j < level.initial_inventory_size[i]; j++)
3653       {
3654         int element = level.initial_inventory_content[i][j];
3655         int collect_count = element_info[element].collect_count_initial;
3656         int k;
3657
3658         if (!IS_CUSTOM_ELEMENT(element))
3659           collect_count = 1;
3660
3661         if (collect_count == 0)
3662           player->inventory_infinite_element = element;
3663         else
3664           for (k = 0; k < collect_count; k++)
3665             if (player->inventory_size < MAX_INVENTORY_SIZE)
3666               player->inventory_element[player->inventory_size++] = element;
3667       }
3668     }
3669
3670     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3671     SnapField(player, 0, 0);
3672
3673     map_player_action[i] = i;
3674   }
3675
3676   network_player_action_received = FALSE;
3677
3678   // initial null action
3679   if (network_playing)
3680     SendToServer_MovePlayer(MV_NONE);
3681
3682   FrameCounter = 0;
3683   TimeFrames = 0;
3684   TimePlayed = 0;
3685   TimeLeft = level.time;
3686   TapeTime = 0;
3687
3688   ScreenMovDir = MV_NONE;
3689   ScreenMovPos = 0;
3690   ScreenGfxPos = 0;
3691
3692   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3693
3694   game.robot_wheel_x = -1;
3695   game.robot_wheel_y = -1;
3696
3697   game.exit_x = -1;
3698   game.exit_y = -1;
3699
3700   game.all_players_gone = FALSE;
3701
3702   game.LevelSolved = FALSE;
3703   game.GameOver = FALSE;
3704
3705   game.GamePlayed = !tape.playing;
3706
3707   game.LevelSolved_GameWon = FALSE;
3708   game.LevelSolved_GameEnd = FALSE;
3709   game.LevelSolved_SaveTape = FALSE;
3710   game.LevelSolved_SaveScore = FALSE;
3711
3712   game.LevelSolved_CountingTime = 0;
3713   game.LevelSolved_CountingScore = 0;
3714   game.LevelSolved_CountingHealth = 0;
3715
3716   game.panel.active = TRUE;
3717
3718   game.no_time_limit = (level.time == 0);
3719
3720   game.yamyam_content_nr = 0;
3721   game.robot_wheel_active = FALSE;
3722   game.magic_wall_active = FALSE;
3723   game.magic_wall_time_left = 0;
3724   game.light_time_left = 0;
3725   game.timegate_time_left = 0;
3726   game.switchgate_pos = 0;
3727   game.wind_direction = level.wind_direction_initial;
3728
3729   game.score = 0;
3730   game.score_final = 0;
3731
3732   game.health = MAX_HEALTH;
3733   game.health_final = MAX_HEALTH;
3734
3735   game.gems_still_needed = level.gems_needed;
3736   game.sokoban_fields_still_needed = 0;
3737   game.sokoban_objects_still_needed = 0;
3738   game.lights_still_needed = 0;
3739   game.players_still_needed = 0;
3740   game.friends_still_needed = 0;
3741
3742   game.lenses_time_left = 0;
3743   game.magnify_time_left = 0;
3744
3745   game.ball_active = level.ball_active_initial;
3746   game.ball_content_nr = 0;
3747
3748   game.explosions_delayed = TRUE;
3749
3750   game.envelope_active = FALSE;
3751
3752   for (i = 0; i < NUM_BELTS; i++)
3753   {
3754     game.belt_dir[i] = MV_NONE;
3755     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3756   }
3757
3758   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3759     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3760
3761 #if DEBUG_INIT_PLAYER
3762   DebugPrintPlayerStatus("Player status at level initialization");
3763 #endif
3764
3765   SCAN_PLAYFIELD(x, y)
3766   {
3767     Tile[x][y] = Last[x][y] = level.field[x][y];
3768     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3769     ChangeDelay[x][y] = 0;
3770     ChangePage[x][y] = -1;
3771     CustomValue[x][y] = 0;              // initialized in InitField()
3772     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3773     AmoebaNr[x][y] = 0;
3774     WasJustMoving[x][y] = 0;
3775     WasJustFalling[x][y] = 0;
3776     CheckCollision[x][y] = 0;
3777     CheckImpact[x][y] = 0;
3778     Stop[x][y] = FALSE;
3779     Pushed[x][y] = FALSE;
3780
3781     ChangeCount[x][y] = 0;
3782     ChangeEvent[x][y] = -1;
3783
3784     ExplodePhase[x][y] = 0;
3785     ExplodeDelay[x][y] = 0;
3786     ExplodeField[x][y] = EX_TYPE_NONE;
3787
3788     RunnerVisit[x][y] = 0;
3789     PlayerVisit[x][y] = 0;
3790
3791     GfxFrame[x][y] = 0;
3792     GfxRandom[x][y] = INIT_GFX_RANDOM();
3793     GfxElement[x][y] = EL_UNDEFINED;
3794     GfxAction[x][y] = ACTION_DEFAULT;
3795     GfxDir[x][y] = MV_NONE;
3796     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3797   }
3798
3799   SCAN_PLAYFIELD(x, y)
3800   {
3801     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3802       emulate_bd = FALSE;
3803     if (emulate_sb && !IS_SB_ELEMENT(Tile[x][y]))
3804       emulate_sb = FALSE;
3805     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3806       emulate_sp = FALSE;
3807
3808     InitField(x, y, TRUE);
3809
3810     ResetGfxAnimation(x, y);
3811   }
3812
3813   InitBeltMovement();
3814
3815   for (i = 0; i < MAX_PLAYERS; i++)
3816   {
3817     struct PlayerInfo *player = &stored_player[i];
3818
3819     // set number of special actions for bored and sleeping animation
3820     player->num_special_action_bored =
3821       get_num_special_action(player->artwork_element,
3822                              ACTION_BORING_1, ACTION_BORING_LAST);
3823     player->num_special_action_sleeping =
3824       get_num_special_action(player->artwork_element,
3825                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3826   }
3827
3828   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3829                     emulate_sb ? EMU_SOKOBAN :
3830                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3831
3832   // initialize type of slippery elements
3833   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3834   {
3835     if (!IS_CUSTOM_ELEMENT(i))
3836     {
3837       // default: elements slip down either to the left or right randomly
3838       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3839
3840       // SP style elements prefer to slip down on the left side
3841       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3842         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3843
3844       // BD style elements prefer to slip down on the left side
3845       if (game.emulation == EMU_BOULDERDASH)
3846         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3847     }
3848   }
3849
3850   // initialize explosion and ignition delay
3851   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3852   {
3853     if (!IS_CUSTOM_ELEMENT(i))
3854     {
3855       int num_phase = 8;
3856       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3857                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3858                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3859       int last_phase = (num_phase + 1) * delay;
3860       int half_phase = (num_phase / 2) * delay;
3861
3862       element_info[i].explosion_delay = last_phase - 1;
3863       element_info[i].ignition_delay = half_phase;
3864
3865       if (i == EL_BLACK_ORB)
3866         element_info[i].ignition_delay = 1;
3867     }
3868   }
3869
3870   // correct non-moving belts to start moving left
3871   for (i = 0; i < NUM_BELTS; i++)
3872     if (game.belt_dir[i] == MV_NONE)
3873       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3874
3875 #if USE_NEW_PLAYER_ASSIGNMENTS
3876   // use preferred player also in local single-player mode
3877   if (!network.enabled && !game.team_mode)
3878   {
3879     int new_index_nr = setup.network_player_nr;
3880
3881     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3882     {
3883       for (i = 0; i < MAX_PLAYERS; i++)
3884         stored_player[i].connected_locally = FALSE;
3885
3886       stored_player[new_index_nr].connected_locally = TRUE;
3887     }
3888   }
3889
3890   for (i = 0; i < MAX_PLAYERS; i++)
3891   {
3892     stored_player[i].connected = FALSE;
3893
3894     // in network game mode, the local player might not be the first player
3895     if (stored_player[i].connected_locally)
3896       local_player = &stored_player[i];
3897   }
3898
3899   if (!network.enabled)
3900     local_player->connected = TRUE;
3901
3902   if (tape.playing)
3903   {
3904     for (i = 0; i < MAX_PLAYERS; i++)
3905       stored_player[i].connected = tape.player_participates[i];
3906   }
3907   else if (network.enabled)
3908   {
3909     // add team mode players connected over the network (needed for correct
3910     // assignment of player figures from level to locally playing players)
3911
3912     for (i = 0; i < MAX_PLAYERS; i++)
3913       if (stored_player[i].connected_network)
3914         stored_player[i].connected = TRUE;
3915   }
3916   else if (game.team_mode)
3917   {
3918     // try to guess locally connected team mode players (needed for correct
3919     // assignment of player figures from level to locally playing players)
3920
3921     for (i = 0; i < MAX_PLAYERS; i++)
3922       if (setup.input[i].use_joystick ||
3923           setup.input[i].key.left != KSYM_UNDEFINED)
3924         stored_player[i].connected = TRUE;
3925   }
3926
3927 #if DEBUG_INIT_PLAYER
3928   DebugPrintPlayerStatus("Player status after level initialization");
3929 #endif
3930
3931 #if DEBUG_INIT_PLAYER
3932   Debug("game:init:player", "Reassigning players ...");
3933 #endif
3934
3935   // check if any connected player was not found in playfield
3936   for (i = 0; i < MAX_PLAYERS; i++)
3937   {
3938     struct PlayerInfo *player = &stored_player[i];
3939
3940     if (player->connected && !player->present)
3941     {
3942       struct PlayerInfo *field_player = NULL;
3943
3944 #if DEBUG_INIT_PLAYER
3945       Debug("game:init:player",
3946             "- looking for field player for player %d ...", i + 1);
3947 #endif
3948
3949       // assign first free player found that is present in the playfield
3950
3951       // first try: look for unmapped playfield player that is not connected
3952       for (j = 0; j < MAX_PLAYERS; j++)
3953         if (field_player == NULL &&
3954             stored_player[j].present &&
3955             !stored_player[j].mapped &&
3956             !stored_player[j].connected)
3957           field_player = &stored_player[j];
3958
3959       // second try: look for *any* unmapped playfield player
3960       for (j = 0; j < MAX_PLAYERS; j++)
3961         if (field_player == NULL &&
3962             stored_player[j].present &&
3963             !stored_player[j].mapped)
3964           field_player = &stored_player[j];
3965
3966       if (field_player != NULL)
3967       {
3968         int jx = field_player->jx, jy = field_player->jy;
3969
3970 #if DEBUG_INIT_PLAYER
3971         Debug("game:init:player", "- found player %d",
3972               field_player->index_nr + 1);
3973 #endif
3974
3975         player->present = FALSE;
3976         player->active = FALSE;
3977
3978         field_player->present = TRUE;
3979         field_player->active = TRUE;
3980
3981         /*
3982         player->initial_element = field_player->initial_element;
3983         player->artwork_element = field_player->artwork_element;
3984
3985         player->block_last_field       = field_player->block_last_field;
3986         player->block_delay_adjustment = field_player->block_delay_adjustment;
3987         */
3988
3989         StorePlayer[jx][jy] = field_player->element_nr;
3990
3991         field_player->jx = field_player->last_jx = jx;
3992         field_player->jy = field_player->last_jy = jy;
3993
3994         if (local_player == player)
3995           local_player = field_player;
3996
3997         map_player_action[field_player->index_nr] = i;
3998
3999         field_player->mapped = TRUE;
4000
4001 #if DEBUG_INIT_PLAYER
4002         Debug("game:init:player", "- map_player_action[%d] == %d",
4003               field_player->index_nr + 1, i + 1);
4004 #endif
4005       }
4006     }
4007
4008     if (player->connected && player->present)
4009       player->mapped = TRUE;
4010   }
4011
4012 #if DEBUG_INIT_PLAYER
4013   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4014 #endif
4015
4016 #else
4017
4018   // check if any connected player was not found in playfield
4019   for (i = 0; i < MAX_PLAYERS; i++)
4020   {
4021     struct PlayerInfo *player = &stored_player[i];
4022
4023     if (player->connected && !player->present)
4024     {
4025       for (j = 0; j < MAX_PLAYERS; j++)
4026       {
4027         struct PlayerInfo *field_player = &stored_player[j];
4028         int jx = field_player->jx, jy = field_player->jy;
4029
4030         // assign first free player found that is present in the playfield
4031         if (field_player->present && !field_player->connected)
4032         {
4033           player->present = TRUE;
4034           player->active = TRUE;
4035
4036           field_player->present = FALSE;
4037           field_player->active = FALSE;
4038
4039           player->initial_element = field_player->initial_element;
4040           player->artwork_element = field_player->artwork_element;
4041
4042           player->block_last_field       = field_player->block_last_field;
4043           player->block_delay_adjustment = field_player->block_delay_adjustment;
4044
4045           StorePlayer[jx][jy] = player->element_nr;
4046
4047           player->jx = player->last_jx = jx;
4048           player->jy = player->last_jy = jy;
4049
4050           break;
4051         }
4052       }
4053     }
4054   }
4055 #endif
4056
4057 #if 0
4058   Debug("game:init:player", "local_player->present == %d",
4059         local_player->present);
4060 #endif
4061
4062   // set focus to local player for network games, else to all players
4063   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4064   game.centered_player_nr_next = game.centered_player_nr;
4065   game.set_centered_player = FALSE;
4066   game.set_centered_player_wrap = FALSE;
4067
4068   if (network_playing && tape.recording)
4069   {
4070     // store client dependent player focus when recording network games
4071     tape.centered_player_nr_next = game.centered_player_nr_next;
4072     tape.set_centered_player = TRUE;
4073   }
4074
4075   if (tape.playing)
4076   {
4077     // when playing a tape, eliminate all players who do not participate
4078
4079 #if USE_NEW_PLAYER_ASSIGNMENTS
4080
4081     if (!game.team_mode)
4082     {
4083       for (i = 0; i < MAX_PLAYERS; i++)
4084       {
4085         if (stored_player[i].active &&
4086             !tape.player_participates[map_player_action[i]])
4087         {
4088           struct PlayerInfo *player = &stored_player[i];
4089           int jx = player->jx, jy = player->jy;
4090
4091 #if DEBUG_INIT_PLAYER
4092           Debug("game:init:player", "Removing player %d at (%d, %d)",
4093                 i + 1, jx, jy);
4094 #endif
4095
4096           player->active = FALSE;
4097           StorePlayer[jx][jy] = 0;
4098           Tile[jx][jy] = EL_EMPTY;
4099         }
4100       }
4101     }
4102
4103 #else
4104
4105     for (i = 0; i < MAX_PLAYERS; i++)
4106     {
4107       if (stored_player[i].active &&
4108           !tape.player_participates[i])
4109       {
4110         struct PlayerInfo *player = &stored_player[i];
4111         int jx = player->jx, jy = player->jy;
4112
4113         player->active = FALSE;
4114         StorePlayer[jx][jy] = 0;
4115         Tile[jx][jy] = EL_EMPTY;
4116       }
4117     }
4118 #endif
4119   }
4120   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4121   {
4122     // when in single player mode, eliminate all but the local player
4123
4124     for (i = 0; i < MAX_PLAYERS; i++)
4125     {
4126       struct PlayerInfo *player = &stored_player[i];
4127
4128       if (player->active && player != local_player)
4129       {
4130         int jx = player->jx, jy = player->jy;
4131
4132         player->active = FALSE;
4133         player->present = FALSE;
4134
4135         StorePlayer[jx][jy] = 0;
4136         Tile[jx][jy] = EL_EMPTY;
4137       }
4138     }
4139   }
4140
4141   for (i = 0; i < MAX_PLAYERS; i++)
4142     if (stored_player[i].active)
4143       game.players_still_needed++;
4144
4145   if (level.solved_by_one_player)
4146     game.players_still_needed = 1;
4147
4148   // when recording the game, store which players take part in the game
4149   if (tape.recording)
4150   {
4151 #if USE_NEW_PLAYER_ASSIGNMENTS
4152     for (i = 0; i < MAX_PLAYERS; i++)
4153       if (stored_player[i].connected)
4154         tape.player_participates[i] = TRUE;
4155 #else
4156     for (i = 0; i < MAX_PLAYERS; i++)
4157       if (stored_player[i].active)
4158         tape.player_participates[i] = TRUE;
4159 #endif
4160   }
4161
4162 #if DEBUG_INIT_PLAYER
4163   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4164 #endif
4165
4166   if (BorderElement == EL_EMPTY)
4167   {
4168     SBX_Left = 0;
4169     SBX_Right = lev_fieldx - SCR_FIELDX;
4170     SBY_Upper = 0;
4171     SBY_Lower = lev_fieldy - SCR_FIELDY;
4172   }
4173   else
4174   {
4175     SBX_Left = -1;
4176     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4177     SBY_Upper = -1;
4178     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4179   }
4180
4181   if (full_lev_fieldx <= SCR_FIELDX)
4182     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4183   if (full_lev_fieldy <= SCR_FIELDY)
4184     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4185
4186   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4187     SBX_Left--;
4188   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4189     SBY_Upper--;
4190
4191   // if local player not found, look for custom element that might create
4192   // the player (make some assumptions about the right custom element)
4193   if (!local_player->present)
4194   {
4195     int start_x = 0, start_y = 0;
4196     int found_rating = 0;
4197     int found_element = EL_UNDEFINED;
4198     int player_nr = local_player->index_nr;
4199
4200     SCAN_PLAYFIELD(x, y)
4201     {
4202       int element = Tile[x][y];
4203       int content;
4204       int xx, yy;
4205       boolean is_player;
4206
4207       if (level.use_start_element[player_nr] &&
4208           level.start_element[player_nr] == element &&
4209           found_rating < 4)
4210       {
4211         start_x = x;
4212         start_y = y;
4213
4214         found_rating = 4;
4215         found_element = element;
4216       }
4217
4218       if (!IS_CUSTOM_ELEMENT(element))
4219         continue;
4220
4221       if (CAN_CHANGE(element))
4222       {
4223         for (i = 0; i < element_info[element].num_change_pages; i++)
4224         {
4225           // check for player created from custom element as single target
4226           content = element_info[element].change_page[i].target_element;
4227           is_player = ELEM_IS_PLAYER(content);
4228
4229           if (is_player && (found_rating < 3 ||
4230                             (found_rating == 3 && element < found_element)))
4231           {
4232             start_x = x;
4233             start_y = y;
4234
4235             found_rating = 3;
4236             found_element = element;
4237           }
4238         }
4239       }
4240
4241       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4242       {
4243         // check for player created from custom element as explosion content
4244         content = element_info[element].content.e[xx][yy];
4245         is_player = ELEM_IS_PLAYER(content);
4246
4247         if (is_player && (found_rating < 2 ||
4248                           (found_rating == 2 && element < found_element)))
4249         {
4250           start_x = x + xx - 1;
4251           start_y = y + yy - 1;
4252
4253           found_rating = 2;
4254           found_element = element;
4255         }
4256
4257         if (!CAN_CHANGE(element))
4258           continue;
4259
4260         for (i = 0; i < element_info[element].num_change_pages; i++)
4261         {
4262           // check for player created from custom element as extended target
4263           content =
4264             element_info[element].change_page[i].target_content.e[xx][yy];
4265
4266           is_player = ELEM_IS_PLAYER(content);
4267
4268           if (is_player && (found_rating < 1 ||
4269                             (found_rating == 1 && element < found_element)))
4270           {
4271             start_x = x + xx - 1;
4272             start_y = y + yy - 1;
4273
4274             found_rating = 1;
4275             found_element = element;
4276           }
4277         }
4278       }
4279     }
4280
4281     scroll_x = SCROLL_POSITION_X(start_x);
4282     scroll_y = SCROLL_POSITION_Y(start_y);
4283   }
4284   else
4285   {
4286     scroll_x = SCROLL_POSITION_X(local_player->jx);
4287     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4288   }
4289
4290   // !!! FIX THIS (START) !!!
4291   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4292   {
4293     InitGameEngine_EM();
4294   }
4295   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4296   {
4297     InitGameEngine_SP();
4298   }
4299   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4300   {
4301     InitGameEngine_MM();
4302   }
4303   else
4304   {
4305     DrawLevel(REDRAW_FIELD);
4306     DrawAllPlayers();
4307
4308     // after drawing the level, correct some elements
4309     if (game.timegate_time_left == 0)
4310       CloseAllOpenTimegates();
4311   }
4312
4313   // blit playfield from scroll buffer to normal back buffer for fading in
4314   BlitScreenToBitmap(backbuffer);
4315   // !!! FIX THIS (END) !!!
4316
4317   DrawMaskedBorder(fade_mask);
4318
4319   FadeIn(fade_mask);
4320
4321 #if 1
4322   // full screen redraw is required at this point in the following cases:
4323   // - special editor door undrawn when game was started from level editor
4324   // - drawing area (playfield) was changed and has to be removed completely
4325   redraw_mask = REDRAW_ALL;
4326   BackToFront();
4327 #endif
4328
4329   if (!game.restart_level)
4330   {
4331     // copy default game door content to main double buffer
4332
4333     // !!! CHECK AGAIN !!!
4334     SetPanelBackground();
4335     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4336     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4337   }
4338
4339   SetPanelBackground();
4340   SetDrawBackgroundMask(REDRAW_DOOR_1);
4341
4342   UpdateAndDisplayGameControlValues();
4343
4344   if (!game.restart_level)
4345   {
4346     UnmapGameButtons();
4347     UnmapTapeButtons();
4348
4349     FreeGameButtons();
4350     CreateGameButtons();
4351
4352     MapGameButtons();
4353     MapTapeButtons();
4354
4355     // copy actual game door content to door double buffer for OpenDoor()
4356     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4357
4358     OpenDoor(DOOR_OPEN_ALL);
4359
4360     KeyboardAutoRepeatOffUnlessAutoplay();
4361
4362 #if DEBUG_INIT_PLAYER
4363     DebugPrintPlayerStatus("Player status (final)");
4364 #endif
4365   }
4366
4367   UnmapAllGadgets();
4368
4369   MapGameButtons();
4370   MapTapeButtons();
4371
4372   if (!game.restart_level && !tape.playing)
4373   {
4374     LevelStats_incPlayed(level_nr);
4375
4376     SaveLevelSetup_SeriesInfo();
4377   }
4378
4379   game.restart_level = FALSE;
4380   game.restart_game_message = NULL;
4381   game.request_active = FALSE;
4382
4383   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4384     InitGameActions_MM();
4385
4386   SaveEngineSnapshotToListInitial();
4387
4388   if (!game.restart_level)
4389   {
4390     PlaySound(SND_GAME_STARTING);
4391
4392     if (setup.sound_music)
4393       PlayLevelMusic();
4394   }
4395 }
4396
4397 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4398                         int actual_player_x, int actual_player_y)
4399 {
4400   // this is used for non-R'n'D game engines to update certain engine values
4401
4402   // needed to determine if sounds are played within the visible screen area
4403   scroll_x = actual_scroll_x;
4404   scroll_y = actual_scroll_y;
4405
4406   // needed to get player position for "follow finger" playing input method
4407   local_player->jx = actual_player_x;
4408   local_player->jy = actual_player_y;
4409 }
4410
4411 void InitMovDir(int x, int y)
4412 {
4413   int i, element = Tile[x][y];
4414   static int xy[4][2] =
4415   {
4416     {  0, +1 },
4417     { +1,  0 },
4418     {  0, -1 },
4419     { -1,  0 }
4420   };
4421   static int direction[3][4] =
4422   {
4423     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4424     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4425     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4426   };
4427
4428   switch (element)
4429   {
4430     case EL_BUG_RIGHT:
4431     case EL_BUG_UP:
4432     case EL_BUG_LEFT:
4433     case EL_BUG_DOWN:
4434       Tile[x][y] = EL_BUG;
4435       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4436       break;
4437
4438     case EL_SPACESHIP_RIGHT:
4439     case EL_SPACESHIP_UP:
4440     case EL_SPACESHIP_LEFT:
4441     case EL_SPACESHIP_DOWN:
4442       Tile[x][y] = EL_SPACESHIP;
4443       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4444       break;
4445
4446     case EL_BD_BUTTERFLY_RIGHT:
4447     case EL_BD_BUTTERFLY_UP:
4448     case EL_BD_BUTTERFLY_LEFT:
4449     case EL_BD_BUTTERFLY_DOWN:
4450       Tile[x][y] = EL_BD_BUTTERFLY;
4451       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4452       break;
4453
4454     case EL_BD_FIREFLY_RIGHT:
4455     case EL_BD_FIREFLY_UP:
4456     case EL_BD_FIREFLY_LEFT:
4457     case EL_BD_FIREFLY_DOWN:
4458       Tile[x][y] = EL_BD_FIREFLY;
4459       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4460       break;
4461
4462     case EL_PACMAN_RIGHT:
4463     case EL_PACMAN_UP:
4464     case EL_PACMAN_LEFT:
4465     case EL_PACMAN_DOWN:
4466       Tile[x][y] = EL_PACMAN;
4467       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4468       break;
4469
4470     case EL_YAMYAM_LEFT:
4471     case EL_YAMYAM_RIGHT:
4472     case EL_YAMYAM_UP:
4473     case EL_YAMYAM_DOWN:
4474       Tile[x][y] = EL_YAMYAM;
4475       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4476       break;
4477
4478     case EL_SP_SNIKSNAK:
4479       MovDir[x][y] = MV_UP;
4480       break;
4481
4482     case EL_SP_ELECTRON:
4483       MovDir[x][y] = MV_LEFT;
4484       break;
4485
4486     case EL_MOLE_LEFT:
4487     case EL_MOLE_RIGHT:
4488     case EL_MOLE_UP:
4489     case EL_MOLE_DOWN:
4490       Tile[x][y] = EL_MOLE;
4491       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4492       break;
4493
4494     case EL_SPRING_LEFT:
4495     case EL_SPRING_RIGHT:
4496       Tile[x][y] = EL_SPRING;
4497       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4498       break;
4499
4500     default:
4501       if (IS_CUSTOM_ELEMENT(element))
4502       {
4503         struct ElementInfo *ei = &element_info[element];
4504         int move_direction_initial = ei->move_direction_initial;
4505         int move_pattern = ei->move_pattern;
4506
4507         if (move_direction_initial == MV_START_PREVIOUS)
4508         {
4509           if (MovDir[x][y] != MV_NONE)
4510             return;
4511
4512           move_direction_initial = MV_START_AUTOMATIC;
4513         }
4514
4515         if (move_direction_initial == MV_START_RANDOM)
4516           MovDir[x][y] = 1 << RND(4);
4517         else if (move_direction_initial & MV_ANY_DIRECTION)
4518           MovDir[x][y] = move_direction_initial;
4519         else if (move_pattern == MV_ALL_DIRECTIONS ||
4520                  move_pattern == MV_TURNING_LEFT ||
4521                  move_pattern == MV_TURNING_RIGHT ||
4522                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4523                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4524                  move_pattern == MV_TURNING_RANDOM)
4525           MovDir[x][y] = 1 << RND(4);
4526         else if (move_pattern == MV_HORIZONTAL)
4527           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4528         else if (move_pattern == MV_VERTICAL)
4529           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4530         else if (move_pattern & MV_ANY_DIRECTION)
4531           MovDir[x][y] = element_info[element].move_pattern;
4532         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4533                  move_pattern == MV_ALONG_RIGHT_SIDE)
4534         {
4535           // use random direction as default start direction
4536           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4537             MovDir[x][y] = 1 << RND(4);
4538
4539           for (i = 0; i < NUM_DIRECTIONS; i++)
4540           {
4541             int x1 = x + xy[i][0];
4542             int y1 = y + xy[i][1];
4543
4544             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4545             {
4546               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4547                 MovDir[x][y] = direction[0][i];
4548               else
4549                 MovDir[x][y] = direction[1][i];
4550
4551               break;
4552             }
4553           }
4554         }                
4555       }
4556       else
4557       {
4558         MovDir[x][y] = 1 << RND(4);
4559
4560         if (element != EL_BUG &&
4561             element != EL_SPACESHIP &&
4562             element != EL_BD_BUTTERFLY &&
4563             element != EL_BD_FIREFLY)
4564           break;
4565
4566         for (i = 0; i < NUM_DIRECTIONS; i++)
4567         {
4568           int x1 = x + xy[i][0];
4569           int y1 = y + xy[i][1];
4570
4571           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4572           {
4573             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4574             {
4575               MovDir[x][y] = direction[0][i];
4576               break;
4577             }
4578             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4579                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4580             {
4581               MovDir[x][y] = direction[1][i];
4582               break;
4583             }
4584           }
4585         }
4586       }
4587       break;
4588   }
4589
4590   GfxDir[x][y] = MovDir[x][y];
4591 }
4592
4593 void InitAmoebaNr(int x, int y)
4594 {
4595   int i;
4596   int group_nr = AmoebaNeighbourNr(x, y);
4597
4598   if (group_nr == 0)
4599   {
4600     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4601     {
4602       if (AmoebaCnt[i] == 0)
4603       {
4604         group_nr = i;
4605         break;
4606       }
4607     }
4608   }
4609
4610   AmoebaNr[x][y] = group_nr;
4611   AmoebaCnt[group_nr]++;
4612   AmoebaCnt2[group_nr]++;
4613 }
4614
4615 static void LevelSolved(void)
4616 {
4617   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4618       game.players_still_needed > 0)
4619     return;
4620
4621   game.LevelSolved = TRUE;
4622   game.GameOver = TRUE;
4623
4624   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4625                       game_em.lev->score :
4626                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4627                       game_mm.score :
4628                       game.score);
4629   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4630                        MM_HEALTH(game_mm.laser_overload_value) :
4631                        game.health);
4632
4633   game.LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed : TimeLeft);
4634   game.LevelSolved_CountingScore = game.score_final;
4635   game.LevelSolved_CountingHealth = game.health_final;
4636 }
4637
4638 void GameWon(void)
4639 {
4640   static int time_count_steps;
4641   static int time, time_final;
4642   static int score, score_final;
4643   static int health, health_final;
4644   static int game_over_delay_1 = 0;
4645   static int game_over_delay_2 = 0;
4646   static int game_over_delay_3 = 0;
4647   int game_over_delay_value_1 = 50;
4648   int game_over_delay_value_2 = 25;
4649   int game_over_delay_value_3 = 50;
4650
4651   if (!game.LevelSolved_GameWon)
4652   {
4653     int i;
4654
4655     // do not start end game actions before the player stops moving (to exit)
4656     if (local_player->active && local_player->MovPos)
4657       return;
4658
4659     game.LevelSolved_GameWon = TRUE;
4660     game.LevelSolved_SaveTape = tape.recording;
4661     game.LevelSolved_SaveScore = !tape.playing;
4662
4663     if (!tape.playing)
4664     {
4665       LevelStats_incSolved(level_nr);
4666
4667       SaveLevelSetup_SeriesInfo();
4668     }
4669
4670     if (tape.auto_play)         // tape might already be stopped here
4671       tape.auto_play_level_solved = TRUE;
4672
4673     TapeStop();
4674
4675     game_over_delay_1 = 0;
4676     game_over_delay_2 = 0;
4677     game_over_delay_3 = game_over_delay_value_3;
4678
4679     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4680     score = score_final = game.score_final;
4681     health = health_final = game.health_final;
4682
4683     if (level.score[SC_TIME_BONUS] > 0)
4684     {
4685       if (TimeLeft > 0)
4686       {
4687         time_final = 0;
4688         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4689       }
4690       else if (game.no_time_limit && TimePlayed < 999)
4691       {
4692         time_final = 999;
4693         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4694       }
4695
4696       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4697
4698       game_over_delay_1 = game_over_delay_value_1;
4699
4700       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4701       {
4702         health_final = 0;
4703         score_final += health * level.score[SC_TIME_BONUS];
4704
4705         game_over_delay_2 = game_over_delay_value_2;
4706       }
4707
4708       game.score_final = score_final;
4709       game.health_final = health_final;
4710     }
4711
4712     if (level_editor_test_game)
4713     {
4714       time = time_final;
4715       score = score_final;
4716
4717       game.LevelSolved_CountingTime = time;
4718       game.LevelSolved_CountingScore = score;
4719
4720       game_panel_controls[GAME_PANEL_TIME].value = time;
4721       game_panel_controls[GAME_PANEL_SCORE].value = score;
4722
4723       DisplayGameControlValues();
4724     }
4725
4726     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4727     {
4728       // check if last player has left the level
4729       if (game.exit_x >= 0 &&
4730           game.exit_y >= 0)
4731       {
4732         int x = game.exit_x;
4733         int y = game.exit_y;
4734         int element = Tile[x][y];
4735
4736         // close exit door after last player
4737         if ((game.all_players_gone &&
4738              (element == EL_EXIT_OPEN ||
4739               element == EL_SP_EXIT_OPEN ||
4740               element == EL_STEEL_EXIT_OPEN)) ||
4741             element == EL_EM_EXIT_OPEN ||
4742             element == EL_EM_STEEL_EXIT_OPEN)
4743         {
4744
4745           Tile[x][y] =
4746             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4747              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4748              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4749              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4750              EL_EM_STEEL_EXIT_CLOSING);
4751
4752           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4753         }
4754
4755         // player disappears
4756         DrawLevelField(x, y);
4757       }
4758
4759       for (i = 0; i < MAX_PLAYERS; i++)
4760       {
4761         struct PlayerInfo *player = &stored_player[i];
4762
4763         if (player->present)
4764         {
4765           RemovePlayer(player);
4766
4767           // player disappears
4768           DrawLevelField(player->jx, player->jy);
4769         }
4770       }
4771     }
4772
4773     PlaySound(SND_GAME_WINNING);
4774   }
4775
4776   if (game_over_delay_1 > 0)
4777   {
4778     game_over_delay_1--;
4779
4780     return;
4781   }
4782
4783   if (time != time_final)
4784   {
4785     int time_to_go = ABS(time_final - time);
4786     int time_count_dir = (time < time_final ? +1 : -1);
4787
4788     if (time_to_go < time_count_steps)
4789       time_count_steps = 1;
4790
4791     time  += time_count_steps * time_count_dir;
4792     score += time_count_steps * level.score[SC_TIME_BONUS];
4793
4794     game.LevelSolved_CountingTime = time;
4795     game.LevelSolved_CountingScore = score;
4796
4797     game_panel_controls[GAME_PANEL_TIME].value = time;
4798     game_panel_controls[GAME_PANEL_SCORE].value = score;
4799
4800     DisplayGameControlValues();
4801
4802     if (time == time_final)
4803       StopSound(SND_GAME_LEVELTIME_BONUS);
4804     else if (setup.sound_loops)
4805       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4806     else
4807       PlaySound(SND_GAME_LEVELTIME_BONUS);
4808
4809     return;
4810   }
4811
4812   if (game_over_delay_2 > 0)
4813   {
4814     game_over_delay_2--;
4815
4816     return;
4817   }
4818
4819   if (health != health_final)
4820   {
4821     int health_count_dir = (health < health_final ? +1 : -1);
4822
4823     health += health_count_dir;
4824     score  += level.score[SC_TIME_BONUS];
4825
4826     game.LevelSolved_CountingHealth = health;
4827     game.LevelSolved_CountingScore = score;
4828
4829     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4830     game_panel_controls[GAME_PANEL_SCORE].value = score;
4831
4832     DisplayGameControlValues();
4833
4834     if (health == health_final)
4835       StopSound(SND_GAME_LEVELTIME_BONUS);
4836     else if (setup.sound_loops)
4837       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4838     else
4839       PlaySound(SND_GAME_LEVELTIME_BONUS);
4840
4841     return;
4842   }
4843
4844   game.panel.active = FALSE;
4845
4846   if (game_over_delay_3 > 0)
4847   {
4848     game_over_delay_3--;
4849
4850     return;
4851   }
4852
4853   GameEnd();
4854 }
4855
4856 void GameEnd(void)
4857 {
4858   // used instead of "level_nr" (needed for network games)
4859   int last_level_nr = levelset.level_nr;
4860   int hi_pos;
4861
4862   game.LevelSolved_GameEnd = TRUE;
4863
4864   if (game.LevelSolved_SaveTape)
4865   {
4866     // make sure that request dialog to save tape does not open door again
4867     if (!global.use_envelope_request)
4868       CloseDoor(DOOR_CLOSE_1);
4869
4870     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4871   }
4872
4873   // if no tape is to be saved, close both doors simultaneously
4874   CloseDoor(DOOR_CLOSE_ALL);
4875
4876   if (level_editor_test_game)
4877   {
4878     SetGameStatus(GAME_MODE_MAIN);
4879
4880     DrawMainMenu();
4881
4882     return;
4883   }
4884
4885   if (!game.LevelSolved_SaveScore)
4886   {
4887     SetGameStatus(GAME_MODE_MAIN);
4888
4889     DrawMainMenu();
4890
4891     return;
4892   }
4893
4894   if (level_nr == leveldir_current->handicap_level)
4895   {
4896     leveldir_current->handicap_level++;
4897
4898     SaveLevelSetup_SeriesInfo();
4899   }
4900
4901   if (setup.increment_levels &&
4902       level_nr < leveldir_current->last_level &&
4903       !network_playing)
4904   {
4905     level_nr++;         // advance to next level
4906     TapeErase();        // start with empty tape
4907
4908     if (setup.auto_play_next_level)
4909     {
4910       LoadLevel(level_nr);
4911
4912       SaveLevelSetup_SeriesInfo();
4913     }
4914   }
4915
4916   hi_pos = NewHiScore(last_level_nr);
4917
4918   if (hi_pos >= 0 && !setup.skip_scores_after_game)
4919   {
4920     SetGameStatus(GAME_MODE_SCORES);
4921
4922     DrawHallOfFame(last_level_nr, hi_pos);
4923   }
4924   else if (setup.auto_play_next_level && setup.increment_levels &&
4925            last_level_nr < leveldir_current->last_level &&
4926            !network_playing)
4927   {
4928     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4929   }
4930   else
4931   {
4932     SetGameStatus(GAME_MODE_MAIN);
4933
4934     DrawMainMenu();
4935   }
4936 }
4937
4938 int NewHiScore(int level_nr)
4939 {
4940   int k, l;
4941   int position = -1;
4942   boolean one_score_entry_per_name = !program.many_scores_per_name;
4943
4944   LoadScore(level_nr);
4945
4946   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4947       game.score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4948     return -1;
4949
4950   for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4951   {
4952     if (game.score_final > highscore[k].Score)
4953     {
4954       // player has made it to the hall of fame
4955
4956       if (k < MAX_SCORE_ENTRIES - 1)
4957       {
4958         int m = MAX_SCORE_ENTRIES - 1;
4959
4960         if (one_score_entry_per_name)
4961         {
4962           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4963             if (strEqual(setup.player_name, highscore[l].Name))
4964               m = l;
4965
4966           if (m == k)   // player's new highscore overwrites his old one
4967             goto put_into_list;
4968         }
4969
4970         for (l = m; l > k; l--)
4971         {
4972           strcpy(highscore[l].Name, highscore[l - 1].Name);
4973           highscore[l].Score = highscore[l - 1].Score;
4974         }
4975       }
4976
4977       put_into_list:
4978
4979       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4980       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4981       highscore[k].Score = game.score_final;
4982       position = k;
4983
4984       break;
4985     }
4986     else if (one_score_entry_per_name &&
4987              !strncmp(setup.player_name, highscore[k].Name,
4988                       MAX_PLAYER_NAME_LEN))
4989       break;    // player already there with a higher score
4990   }
4991
4992   if (position >= 0) 
4993     SaveScore(level_nr);
4994
4995   return position;
4996 }
4997
4998 static int getElementMoveStepsizeExt(int x, int y, int direction)
4999 {
5000   int element = Tile[x][y];
5001   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5002   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5003   int horiz_move = (dx != 0);
5004   int sign = (horiz_move ? dx : dy);
5005   int step = sign * element_info[element].move_stepsize;
5006
5007   // special values for move stepsize for spring and things on conveyor belt
5008   if (horiz_move)
5009   {
5010     if (CAN_FALL(element) &&
5011         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5012       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5013     else if (element == EL_SPRING)
5014       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5015   }
5016
5017   return step;
5018 }
5019
5020 static int getElementMoveStepsize(int x, int y)
5021 {
5022   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5023 }
5024
5025 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5026 {
5027   if (player->GfxAction != action || player->GfxDir != dir)
5028   {
5029     player->GfxAction = action;
5030     player->GfxDir = dir;
5031     player->Frame = 0;
5032     player->StepFrame = 0;
5033   }
5034 }
5035
5036 static void ResetGfxFrame(int x, int y)
5037 {
5038   // profiling showed that "autotest" spends 10~20% of its time in this function
5039   if (DrawingDeactivatedField())
5040     return;
5041
5042   int element = Tile[x][y];
5043   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5044
5045   if (graphic_info[graphic].anim_global_sync)
5046     GfxFrame[x][y] = FrameCounter;
5047   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5048     GfxFrame[x][y] = CustomValue[x][y];
5049   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5050     GfxFrame[x][y] = element_info[element].collect_score;
5051   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5052     GfxFrame[x][y] = ChangeDelay[x][y];
5053 }
5054
5055 static void ResetGfxAnimation(int x, int y)
5056 {
5057   GfxAction[x][y] = ACTION_DEFAULT;
5058   GfxDir[x][y] = MovDir[x][y];
5059   GfxFrame[x][y] = 0;
5060
5061   ResetGfxFrame(x, y);
5062 }
5063
5064 static void ResetRandomAnimationValue(int x, int y)
5065 {
5066   GfxRandom[x][y] = INIT_GFX_RANDOM();
5067 }
5068
5069 static void InitMovingField(int x, int y, int direction)
5070 {
5071   int element = Tile[x][y];
5072   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5073   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5074   int newx = x + dx;
5075   int newy = y + dy;
5076   boolean is_moving_before, is_moving_after;
5077
5078   // check if element was/is moving or being moved before/after mode change
5079   is_moving_before = (WasJustMoving[x][y] != 0);
5080   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5081
5082   // reset animation only for moving elements which change direction of moving
5083   // or which just started or stopped moving
5084   // (else CEs with property "can move" / "not moving" are reset each frame)
5085   if (is_moving_before != is_moving_after ||
5086       direction != MovDir[x][y])
5087     ResetGfxAnimation(x, y);
5088
5089   MovDir[x][y] = direction;
5090   GfxDir[x][y] = direction;
5091
5092   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5093                      direction == MV_DOWN && CAN_FALL(element) ?
5094                      ACTION_FALLING : ACTION_MOVING);
5095
5096   // this is needed for CEs with property "can move" / "not moving"
5097
5098   if (is_moving_after)
5099   {
5100     if (Tile[newx][newy] == EL_EMPTY)
5101       Tile[newx][newy] = EL_BLOCKED;
5102
5103     MovDir[newx][newy] = MovDir[x][y];
5104
5105     CustomValue[newx][newy] = CustomValue[x][y];
5106
5107     GfxFrame[newx][newy] = GfxFrame[x][y];
5108     GfxRandom[newx][newy] = GfxRandom[x][y];
5109     GfxAction[newx][newy] = GfxAction[x][y];
5110     GfxDir[newx][newy] = GfxDir[x][y];
5111   }
5112 }
5113
5114 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5115 {
5116   int direction = MovDir[x][y];
5117   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5118   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5119
5120   *goes_to_x = newx;
5121   *goes_to_y = newy;
5122 }
5123
5124 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5125 {
5126   int oldx = x, oldy = y;
5127   int direction = MovDir[x][y];
5128
5129   if (direction == MV_LEFT)
5130     oldx++;
5131   else if (direction == MV_RIGHT)
5132     oldx--;
5133   else if (direction == MV_UP)
5134     oldy++;
5135   else if (direction == MV_DOWN)
5136     oldy--;
5137
5138   *comes_from_x = oldx;
5139   *comes_from_y = oldy;
5140 }
5141
5142 static int MovingOrBlocked2Element(int x, int y)
5143 {
5144   int element = Tile[x][y];
5145
5146   if (element == EL_BLOCKED)
5147   {
5148     int oldx, oldy;
5149
5150     Blocked2Moving(x, y, &oldx, &oldy);
5151     return Tile[oldx][oldy];
5152   }
5153   else
5154     return element;
5155 }
5156
5157 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5158 {
5159   // like MovingOrBlocked2Element(), but if element is moving
5160   // and (x,y) is the field the moving element is just leaving,
5161   // return EL_BLOCKED instead of the element value
5162   int element = Tile[x][y];
5163
5164   if (IS_MOVING(x, y))
5165   {
5166     if (element == EL_BLOCKED)
5167     {
5168       int oldx, oldy;
5169
5170       Blocked2Moving(x, y, &oldx, &oldy);
5171       return Tile[oldx][oldy];
5172     }
5173     else
5174       return EL_BLOCKED;
5175   }
5176   else
5177     return element;
5178 }
5179
5180 static void RemoveField(int x, int y)
5181 {
5182   Tile[x][y] = EL_EMPTY;
5183
5184   MovPos[x][y] = 0;
5185   MovDir[x][y] = 0;
5186   MovDelay[x][y] = 0;
5187
5188   CustomValue[x][y] = 0;
5189
5190   AmoebaNr[x][y] = 0;
5191   ChangeDelay[x][y] = 0;
5192   ChangePage[x][y] = -1;
5193   Pushed[x][y] = FALSE;
5194
5195   GfxElement[x][y] = EL_UNDEFINED;
5196   GfxAction[x][y] = ACTION_DEFAULT;
5197   GfxDir[x][y] = MV_NONE;
5198 }
5199
5200 static void RemoveMovingField(int x, int y)
5201 {
5202   int oldx = x, oldy = y, newx = x, newy = y;
5203   int element = Tile[x][y];
5204   int next_element = EL_UNDEFINED;
5205
5206   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5207     return;
5208
5209   if (IS_MOVING(x, y))
5210   {
5211     Moving2Blocked(x, y, &newx, &newy);
5212
5213     if (Tile[newx][newy] != EL_BLOCKED)
5214     {
5215       // element is moving, but target field is not free (blocked), but
5216       // already occupied by something different (example: acid pool);
5217       // in this case, only remove the moving field, but not the target
5218
5219       RemoveField(oldx, oldy);
5220
5221       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5222
5223       TEST_DrawLevelField(oldx, oldy);
5224
5225       return;
5226     }
5227   }
5228   else if (element == EL_BLOCKED)
5229   {
5230     Blocked2Moving(x, y, &oldx, &oldy);
5231     if (!IS_MOVING(oldx, oldy))
5232       return;
5233   }
5234
5235   if (element == EL_BLOCKED &&
5236       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5237        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5238        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5239        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5240        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5241        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5242     next_element = get_next_element(Tile[oldx][oldy]);
5243
5244   RemoveField(oldx, oldy);
5245   RemoveField(newx, newy);
5246
5247   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5248
5249   if (next_element != EL_UNDEFINED)
5250     Tile[oldx][oldy] = next_element;
5251
5252   TEST_DrawLevelField(oldx, oldy);
5253   TEST_DrawLevelField(newx, newy);
5254 }
5255
5256 void DrawDynamite(int x, int y)
5257 {
5258   int sx = SCREENX(x), sy = SCREENY(y);
5259   int graphic = el2img(Tile[x][y]);
5260   int frame;
5261
5262   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5263     return;
5264
5265   if (IS_WALKABLE_INSIDE(Back[x][y]))
5266     return;
5267
5268   if (Back[x][y])
5269     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5270   else if (Store[x][y])
5271     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5272
5273   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5274
5275   if (Back[x][y] || Store[x][y])
5276     DrawGraphicThruMask(sx, sy, graphic, frame);
5277   else
5278     DrawGraphic(sx, sy, graphic, frame);
5279 }
5280
5281 static void CheckDynamite(int x, int y)
5282 {
5283   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5284   {
5285     MovDelay[x][y]--;
5286
5287     if (MovDelay[x][y] != 0)
5288     {
5289       DrawDynamite(x, y);
5290       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5291
5292       return;
5293     }
5294   }
5295
5296   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5297
5298   Bang(x, y);
5299 }
5300
5301 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5302 {
5303   boolean num_checked_players = 0;
5304   int i;
5305
5306   for (i = 0; i < MAX_PLAYERS; i++)
5307   {
5308     if (stored_player[i].active)
5309     {
5310       int sx = stored_player[i].jx;
5311       int sy = stored_player[i].jy;
5312
5313       if (num_checked_players == 0)
5314       {
5315         *sx1 = *sx2 = sx;
5316         *sy1 = *sy2 = sy;
5317       }
5318       else
5319       {
5320         *sx1 = MIN(*sx1, sx);
5321         *sy1 = MIN(*sy1, sy);
5322         *sx2 = MAX(*sx2, sx);
5323         *sy2 = MAX(*sy2, sy);
5324       }
5325
5326       num_checked_players++;
5327     }
5328   }
5329 }
5330
5331 static boolean checkIfAllPlayersFitToScreen_RND(void)
5332 {
5333   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5334
5335   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5336
5337   return (sx2 - sx1 < SCR_FIELDX &&
5338           sy2 - sy1 < SCR_FIELDY);
5339 }
5340
5341 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5342 {
5343   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5344
5345   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5346
5347   *sx = (sx1 + sx2) / 2;
5348   *sy = (sy1 + sy2) / 2;
5349 }
5350
5351 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5352                                boolean center_screen, boolean quick_relocation)
5353 {
5354   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5355   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5356   boolean no_delay = (tape.warp_forward);
5357   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5358   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5359   int new_scroll_x, new_scroll_y;
5360
5361   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5362   {
5363     // case 1: quick relocation inside visible screen (without scrolling)
5364
5365     RedrawPlayfield();
5366
5367     return;
5368   }
5369
5370   if (!level.shifted_relocation || center_screen)
5371   {
5372     // relocation _with_ centering of screen
5373
5374     new_scroll_x = SCROLL_POSITION_X(x);
5375     new_scroll_y = SCROLL_POSITION_Y(y);
5376   }
5377   else
5378   {
5379     // relocation _without_ centering of screen
5380
5381     int center_scroll_x = SCROLL_POSITION_X(old_x);
5382     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5383     int offset_x = x + (scroll_x - center_scroll_x);
5384     int offset_y = y + (scroll_y - center_scroll_y);
5385
5386     // for new screen position, apply previous offset to center position
5387     new_scroll_x = SCROLL_POSITION_X(offset_x);
5388     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5389   }
5390
5391   if (quick_relocation)
5392   {
5393     // case 2: quick relocation (redraw without visible scrolling)
5394
5395     scroll_x = new_scroll_x;
5396     scroll_y = new_scroll_y;
5397
5398     RedrawPlayfield();
5399
5400     return;
5401   }
5402
5403   // case 3: visible relocation (with scrolling to new position)
5404
5405   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5406
5407   SetVideoFrameDelay(wait_delay_value);
5408
5409   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5410   {
5411     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5412     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5413
5414     if (dx == 0 && dy == 0)             // no scrolling needed at all
5415       break;
5416
5417     scroll_x -= dx;
5418     scroll_y -= dy;
5419
5420     // set values for horizontal/vertical screen scrolling (half tile size)
5421     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5422     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5423     int pos_x = dx * TILEX / 2;
5424     int pos_y = dy * TILEY / 2;
5425     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5426     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5427
5428     ScrollLevel(dx, dy);
5429     DrawAllPlayers();
5430
5431     // scroll in two steps of half tile size to make things smoother
5432     BlitScreenToBitmapExt_RND(window, fx, fy);
5433
5434     // scroll second step to align at full tile size
5435     BlitScreenToBitmap(window);
5436   }
5437
5438   DrawAllPlayers();
5439   BackToFront();
5440
5441   SetVideoFrameDelay(frame_delay_value_old);
5442 }
5443
5444 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5445 {
5446   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5447   int player_nr = GET_PLAYER_NR(el_player);
5448   struct PlayerInfo *player = &stored_player[player_nr];
5449   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5450   boolean no_delay = (tape.warp_forward);
5451   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5452   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5453   int old_jx = player->jx;
5454   int old_jy = player->jy;
5455   int old_element = Tile[old_jx][old_jy];
5456   int element = Tile[jx][jy];
5457   boolean player_relocated = (old_jx != jx || old_jy != jy);
5458
5459   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5460   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5461   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5462   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5463   int leave_side_horiz = move_dir_horiz;
5464   int leave_side_vert  = move_dir_vert;
5465   int enter_side = enter_side_horiz | enter_side_vert;
5466   int leave_side = leave_side_horiz | leave_side_vert;
5467
5468   if (player->buried)           // do not reanimate dead player
5469     return;
5470
5471   if (!player_relocated)        // no need to relocate the player
5472     return;
5473
5474   if (IS_PLAYER(jx, jy))        // player already placed at new position
5475   {
5476     RemoveField(jx, jy);        // temporarily remove newly placed player
5477     DrawLevelField(jx, jy);
5478   }
5479
5480   if (player->present)
5481   {
5482     while (player->MovPos)
5483     {
5484       ScrollPlayer(player, SCROLL_GO_ON);
5485       ScrollScreen(NULL, SCROLL_GO_ON);
5486
5487       AdvanceFrameAndPlayerCounters(player->index_nr);
5488
5489       DrawPlayer(player);
5490
5491       BackToFront_WithFrameDelay(wait_delay_value);
5492     }
5493
5494     DrawPlayer(player);         // needed here only to cleanup last field
5495     DrawLevelField(player->jx, player->jy);     // remove player graphic
5496
5497     player->is_moving = FALSE;
5498   }
5499
5500   if (IS_CUSTOM_ELEMENT(old_element))
5501     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5502                                CE_LEFT_BY_PLAYER,
5503                                player->index_bit, leave_side);
5504
5505   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5506                                       CE_PLAYER_LEAVES_X,
5507                                       player->index_bit, leave_side);
5508
5509   Tile[jx][jy] = el_player;
5510   InitPlayerField(jx, jy, el_player, TRUE);
5511
5512   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5513      possible that the relocation target field did not contain a player element,
5514      but a walkable element, to which the new player was relocated -- in this
5515      case, restore that (already initialized!) element on the player field */
5516   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5517   {
5518     Tile[jx][jy] = element;     // restore previously existing element
5519   }
5520
5521   // only visually relocate centered player
5522   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5523                      FALSE, level.instant_relocation);
5524
5525   TestIfPlayerTouchesBadThing(jx, jy);
5526   TestIfPlayerTouchesCustomElement(jx, jy);
5527
5528   if (IS_CUSTOM_ELEMENT(element))
5529     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5530                                player->index_bit, enter_side);
5531
5532   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5533                                       player->index_bit, enter_side);
5534
5535   if (player->is_switching)
5536   {
5537     /* ensure that relocation while still switching an element does not cause
5538        a new element to be treated as also switched directly after relocation
5539        (this is important for teleporter switches that teleport the player to
5540        a place where another teleporter switch is in the same direction, which
5541        would then incorrectly be treated as immediately switched before the
5542        direction key that caused the switch was released) */
5543
5544     player->switch_x += jx - old_jx;
5545     player->switch_y += jy - old_jy;
5546   }
5547 }
5548
5549 static void Explode(int ex, int ey, int phase, int mode)
5550 {
5551   int x, y;
5552   int last_phase;
5553   int border_element;
5554
5555   // !!! eliminate this variable !!!
5556   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5557
5558   if (game.explosions_delayed)
5559   {
5560     ExplodeField[ex][ey] = mode;
5561     return;
5562   }
5563
5564   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5565   {
5566     int center_element = Tile[ex][ey];
5567     int artwork_element, explosion_element;     // set these values later
5568
5569     // remove things displayed in background while burning dynamite
5570     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5571       Back[ex][ey] = 0;
5572
5573     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5574     {
5575       // put moving element to center field (and let it explode there)
5576       center_element = MovingOrBlocked2Element(ex, ey);
5577       RemoveMovingField(ex, ey);
5578       Tile[ex][ey] = center_element;
5579     }
5580
5581     // now "center_element" is finally determined -- set related values now
5582     artwork_element = center_element;           // for custom player artwork
5583     explosion_element = center_element;         // for custom player artwork
5584
5585     if (IS_PLAYER(ex, ey))
5586     {
5587       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5588
5589       artwork_element = stored_player[player_nr].artwork_element;
5590
5591       if (level.use_explosion_element[player_nr])
5592       {
5593         explosion_element = level.explosion_element[player_nr];
5594         artwork_element = explosion_element;
5595       }
5596     }
5597
5598     if (mode == EX_TYPE_NORMAL ||
5599         mode == EX_TYPE_CENTER ||
5600         mode == EX_TYPE_CROSS)
5601       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5602
5603     last_phase = element_info[explosion_element].explosion_delay + 1;
5604
5605     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5606     {
5607       int xx = x - ex + 1;
5608       int yy = y - ey + 1;
5609       int element;
5610
5611       if (!IN_LEV_FIELD(x, y) ||
5612           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5613           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5614         continue;
5615
5616       element = Tile[x][y];
5617
5618       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5619       {
5620         element = MovingOrBlocked2Element(x, y);
5621
5622         if (!IS_EXPLOSION_PROOF(element))
5623           RemoveMovingField(x, y);
5624       }
5625
5626       // indestructible elements can only explode in center (but not flames)
5627       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5628                                            mode == EX_TYPE_BORDER)) ||
5629           element == EL_FLAMES)
5630         continue;
5631
5632       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5633          behaviour, for example when touching a yamyam that explodes to rocks
5634          with active deadly shield, a rock is created under the player !!! */
5635       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5636 #if 0
5637       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5638           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5639            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5640 #else
5641       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5642 #endif
5643       {
5644         if (IS_ACTIVE_BOMB(element))
5645         {
5646           // re-activate things under the bomb like gate or penguin
5647           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5648           Back[x][y] = 0;
5649         }
5650
5651         continue;
5652       }
5653
5654       // save walkable background elements while explosion on same tile
5655       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5656           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5657         Back[x][y] = element;
5658
5659       // ignite explodable elements reached by other explosion
5660       if (element == EL_EXPLOSION)
5661         element = Store2[x][y];
5662
5663       if (AmoebaNr[x][y] &&
5664           (element == EL_AMOEBA_FULL ||
5665            element == EL_BD_AMOEBA ||
5666            element == EL_AMOEBA_GROWING))
5667       {
5668         AmoebaCnt[AmoebaNr[x][y]]--;
5669         AmoebaCnt2[AmoebaNr[x][y]]--;
5670       }
5671
5672       RemoveField(x, y);
5673
5674       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5675       {
5676         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5677
5678         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5679
5680         if (PLAYERINFO(ex, ey)->use_murphy)
5681           Store[x][y] = EL_EMPTY;
5682       }
5683
5684       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5685       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5686       else if (ELEM_IS_PLAYER(center_element))
5687         Store[x][y] = EL_EMPTY;
5688       else if (center_element == EL_YAMYAM)
5689         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5690       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5691         Store[x][y] = element_info[center_element].content.e[xx][yy];
5692 #if 1
5693       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5694       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5695       // otherwise) -- FIX THIS !!!
5696       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5697         Store[x][y] = element_info[element].content.e[1][1];
5698 #else
5699       else if (!CAN_EXPLODE(element))
5700         Store[x][y] = element_info[element].content.e[1][1];
5701 #endif
5702       else
5703         Store[x][y] = EL_EMPTY;
5704
5705       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5706           center_element == EL_AMOEBA_TO_DIAMOND)
5707         Store2[x][y] = element;
5708
5709       Tile[x][y] = EL_EXPLOSION;
5710       GfxElement[x][y] = artwork_element;
5711
5712       ExplodePhase[x][y] = 1;
5713       ExplodeDelay[x][y] = last_phase;
5714
5715       Stop[x][y] = TRUE;
5716     }
5717
5718     if (center_element == EL_YAMYAM)
5719       game.yamyam_content_nr =
5720         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5721
5722     return;
5723   }
5724
5725   if (Stop[ex][ey])
5726     return;
5727
5728   x = ex;
5729   y = ey;
5730
5731   if (phase == 1)
5732     GfxFrame[x][y] = 0;         // restart explosion animation
5733
5734   last_phase = ExplodeDelay[x][y];
5735
5736   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5737
5738   // this can happen if the player leaves an explosion just in time
5739   if (GfxElement[x][y] == EL_UNDEFINED)
5740     GfxElement[x][y] = EL_EMPTY;
5741
5742   border_element = Store2[x][y];
5743   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5744     border_element = StorePlayer[x][y];
5745
5746   if (phase == element_info[border_element].ignition_delay ||
5747       phase == last_phase)
5748   {
5749     boolean border_explosion = FALSE;
5750
5751     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5752         !PLAYER_EXPLOSION_PROTECTED(x, y))
5753     {
5754       KillPlayerUnlessExplosionProtected(x, y);
5755       border_explosion = TRUE;
5756     }
5757     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5758     {
5759       Tile[x][y] = Store2[x][y];
5760       Store2[x][y] = 0;
5761       Bang(x, y);
5762       border_explosion = TRUE;
5763     }
5764     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5765     {
5766       AmoebaToDiamond(x, y);
5767       Store2[x][y] = 0;
5768       border_explosion = TRUE;
5769     }
5770
5771     // if an element just explodes due to another explosion (chain-reaction),
5772     // do not immediately end the new explosion when it was the last frame of
5773     // the explosion (as it would be done in the following "if"-statement!)
5774     if (border_explosion && phase == last_phase)
5775       return;
5776   }
5777
5778   if (phase == last_phase)
5779   {
5780     int element;
5781
5782     element = Tile[x][y] = Store[x][y];
5783     Store[x][y] = Store2[x][y] = 0;
5784     GfxElement[x][y] = EL_UNDEFINED;
5785
5786     // player can escape from explosions and might therefore be still alive
5787     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5788         element <= EL_PLAYER_IS_EXPLODING_4)
5789     {
5790       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5791       int explosion_element = EL_PLAYER_1 + player_nr;
5792       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5793       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5794
5795       if (level.use_explosion_element[player_nr])
5796         explosion_element = level.explosion_element[player_nr];
5797
5798       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5799                     element_info[explosion_element].content.e[xx][yy]);
5800     }
5801
5802     // restore probably existing indestructible background element
5803     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5804       element = Tile[x][y] = Back[x][y];
5805     Back[x][y] = 0;
5806
5807     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5808     GfxDir[x][y] = MV_NONE;
5809     ChangeDelay[x][y] = 0;
5810     ChangePage[x][y] = -1;
5811
5812     CustomValue[x][y] = 0;
5813
5814     InitField_WithBug2(x, y, FALSE);
5815
5816     TEST_DrawLevelField(x, y);
5817
5818     TestIfElementTouchesCustomElement(x, y);
5819
5820     if (GFX_CRUMBLED(element))
5821       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5822
5823     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5824       StorePlayer[x][y] = 0;
5825
5826     if (ELEM_IS_PLAYER(element))
5827       RelocatePlayer(x, y, element);
5828   }
5829   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5830   {
5831     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5832     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5833
5834     if (phase == delay)
5835       TEST_DrawLevelFieldCrumbled(x, y);
5836
5837     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5838     {
5839       DrawLevelElement(x, y, Back[x][y]);
5840       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5841     }
5842     else if (IS_WALKABLE_UNDER(Back[x][y]))
5843     {
5844       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5845       DrawLevelElementThruMask(x, y, Back[x][y]);
5846     }
5847     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5848       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5849   }
5850 }
5851
5852 static void DynaExplode(int ex, int ey)
5853 {
5854   int i, j;
5855   int dynabomb_element = Tile[ex][ey];
5856   int dynabomb_size = 1;
5857   boolean dynabomb_xl = FALSE;
5858   struct PlayerInfo *player;
5859   static int xy[4][2] =
5860   {
5861     { 0, -1 },
5862     { -1, 0 },
5863     { +1, 0 },
5864     { 0, +1 }
5865   };
5866
5867   if (IS_ACTIVE_BOMB(dynabomb_element))
5868   {
5869     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5870     dynabomb_size = player->dynabomb_size;
5871     dynabomb_xl = player->dynabomb_xl;
5872     player->dynabombs_left++;
5873   }
5874
5875   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5876
5877   for (i = 0; i < NUM_DIRECTIONS; i++)
5878   {
5879     for (j = 1; j <= dynabomb_size; j++)
5880     {
5881       int x = ex + j * xy[i][0];
5882       int y = ey + j * xy[i][1];
5883       int element;
5884
5885       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
5886         break;
5887
5888       element = Tile[x][y];
5889
5890       // do not restart explosions of fields with active bombs
5891       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5892         continue;
5893
5894       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5895
5896       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5897           !IS_DIGGABLE(element) && !dynabomb_xl)
5898         break;
5899     }
5900   }
5901 }
5902
5903 void Bang(int x, int y)
5904 {
5905   int element = MovingOrBlocked2Element(x, y);
5906   int explosion_type = EX_TYPE_NORMAL;
5907
5908   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5909   {
5910     struct PlayerInfo *player = PLAYERINFO(x, y);
5911
5912     element = Tile[x][y] = player->initial_element;
5913
5914     if (level.use_explosion_element[player->index_nr])
5915     {
5916       int explosion_element = level.explosion_element[player->index_nr];
5917
5918       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5919         explosion_type = EX_TYPE_CROSS;
5920       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5921         explosion_type = EX_TYPE_CENTER;
5922     }
5923   }
5924
5925   switch (element)
5926   {
5927     case EL_BUG:
5928     case EL_SPACESHIP:
5929     case EL_BD_BUTTERFLY:
5930     case EL_BD_FIREFLY:
5931     case EL_YAMYAM:
5932     case EL_DARK_YAMYAM:
5933     case EL_ROBOT:
5934     case EL_PACMAN:
5935     case EL_MOLE:
5936       RaiseScoreElement(element);
5937       break;
5938
5939     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5940     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5941     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5942     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5943     case EL_DYNABOMB_INCREASE_NUMBER:
5944     case EL_DYNABOMB_INCREASE_SIZE:
5945     case EL_DYNABOMB_INCREASE_POWER:
5946       explosion_type = EX_TYPE_DYNA;
5947       break;
5948
5949     case EL_DC_LANDMINE:
5950       explosion_type = EX_TYPE_CENTER;
5951       break;
5952
5953     case EL_PENGUIN:
5954     case EL_LAMP:
5955     case EL_LAMP_ACTIVE:
5956     case EL_AMOEBA_TO_DIAMOND:
5957       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
5958         explosion_type = EX_TYPE_CENTER;
5959       break;
5960
5961     default:
5962       if (element_info[element].explosion_type == EXPLODES_CROSS)
5963         explosion_type = EX_TYPE_CROSS;
5964       else if (element_info[element].explosion_type == EXPLODES_1X1)
5965         explosion_type = EX_TYPE_CENTER;
5966       break;
5967   }
5968
5969   if (explosion_type == EX_TYPE_DYNA)
5970     DynaExplode(x, y);
5971   else
5972     Explode(x, y, EX_PHASE_START, explosion_type);
5973
5974   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5975 }
5976
5977 static void SplashAcid(int x, int y)
5978 {
5979   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5980       (!IN_LEV_FIELD(x - 1, y - 2) ||
5981        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5982     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5983
5984   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5985       (!IN_LEV_FIELD(x + 1, y - 2) ||
5986        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5987     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5988
5989   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5990 }
5991
5992 static void InitBeltMovement(void)
5993 {
5994   static int belt_base_element[4] =
5995   {
5996     EL_CONVEYOR_BELT_1_LEFT,
5997     EL_CONVEYOR_BELT_2_LEFT,
5998     EL_CONVEYOR_BELT_3_LEFT,
5999     EL_CONVEYOR_BELT_4_LEFT
6000   };
6001   static int belt_base_active_element[4] =
6002   {
6003     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6004     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6005     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6006     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6007   };
6008
6009   int x, y, i, j;
6010
6011   // set frame order for belt animation graphic according to belt direction
6012   for (i = 0; i < NUM_BELTS; i++)
6013   {
6014     int belt_nr = i;
6015
6016     for (j = 0; j < NUM_BELT_PARTS; j++)
6017     {
6018       int element = belt_base_active_element[belt_nr] + j;
6019       int graphic_1 = el2img(element);
6020       int graphic_2 = el2panelimg(element);
6021
6022       if (game.belt_dir[i] == MV_LEFT)
6023       {
6024         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6025         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6026       }
6027       else
6028       {
6029         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6030         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6031       }
6032     }
6033   }
6034
6035   SCAN_PLAYFIELD(x, y)
6036   {
6037     int element = Tile[x][y];
6038
6039     for (i = 0; i < NUM_BELTS; i++)
6040     {
6041       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6042       {
6043         int e_belt_nr = getBeltNrFromBeltElement(element);
6044         int belt_nr = i;
6045
6046         if (e_belt_nr == belt_nr)
6047         {
6048           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6049
6050           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6051         }
6052       }
6053     }
6054   }
6055 }
6056
6057 static void ToggleBeltSwitch(int x, int y)
6058 {
6059   static int belt_base_element[4] =
6060   {
6061     EL_CONVEYOR_BELT_1_LEFT,
6062     EL_CONVEYOR_BELT_2_LEFT,
6063     EL_CONVEYOR_BELT_3_LEFT,
6064     EL_CONVEYOR_BELT_4_LEFT
6065   };
6066   static int belt_base_active_element[4] =
6067   {
6068     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6069     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6070     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6071     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6072   };
6073   static int belt_base_switch_element[4] =
6074   {
6075     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6076     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6077     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6078     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6079   };
6080   static int belt_move_dir[4] =
6081   {
6082     MV_LEFT,
6083     MV_NONE,
6084     MV_RIGHT,
6085     MV_NONE,
6086   };
6087
6088   int element = Tile[x][y];
6089   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6090   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6091   int belt_dir = belt_move_dir[belt_dir_nr];
6092   int xx, yy, i;
6093
6094   if (!IS_BELT_SWITCH(element))
6095     return;
6096
6097   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6098   game.belt_dir[belt_nr] = belt_dir;
6099
6100   if (belt_dir_nr == 3)
6101     belt_dir_nr = 1;
6102
6103   // set frame order for belt animation graphic according to belt direction
6104   for (i = 0; i < NUM_BELT_PARTS; i++)
6105   {
6106     int element = belt_base_active_element[belt_nr] + i;
6107     int graphic_1 = el2img(element);
6108     int graphic_2 = el2panelimg(element);
6109
6110     if (belt_dir == MV_LEFT)
6111     {
6112       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6113       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6114     }
6115     else
6116     {
6117       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6118       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6119     }
6120   }
6121
6122   SCAN_PLAYFIELD(xx, yy)
6123   {
6124     int element = Tile[xx][yy];
6125
6126     if (IS_BELT_SWITCH(element))
6127     {
6128       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6129
6130       if (e_belt_nr == belt_nr)
6131       {
6132         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6133         TEST_DrawLevelField(xx, yy);
6134       }
6135     }
6136     else if (IS_BELT(element) && belt_dir != MV_NONE)
6137     {
6138       int e_belt_nr = getBeltNrFromBeltElement(element);
6139
6140       if (e_belt_nr == belt_nr)
6141       {
6142         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6143
6144         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6145         TEST_DrawLevelField(xx, yy);
6146       }
6147     }
6148     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6149     {
6150       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6151
6152       if (e_belt_nr == belt_nr)
6153       {
6154         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6155
6156         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6157         TEST_DrawLevelField(xx, yy);
6158       }
6159     }
6160   }
6161 }
6162
6163 static void ToggleSwitchgateSwitch(int x, int y)
6164 {
6165   int xx, yy;
6166
6167   game.switchgate_pos = !game.switchgate_pos;
6168
6169   SCAN_PLAYFIELD(xx, yy)
6170   {
6171     int element = Tile[xx][yy];
6172
6173     if (element == EL_SWITCHGATE_SWITCH_UP)
6174     {
6175       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6176       TEST_DrawLevelField(xx, yy);
6177     }
6178     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6179     {
6180       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6181       TEST_DrawLevelField(xx, yy);
6182     }
6183     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6184     {
6185       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6186       TEST_DrawLevelField(xx, yy);
6187     }
6188     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6189     {
6190       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6191       TEST_DrawLevelField(xx, yy);
6192     }
6193     else if (element == EL_SWITCHGATE_OPEN ||
6194              element == EL_SWITCHGATE_OPENING)
6195     {
6196       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6197
6198       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6199     }
6200     else if (element == EL_SWITCHGATE_CLOSED ||
6201              element == EL_SWITCHGATE_CLOSING)
6202     {
6203       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6204
6205       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6206     }
6207   }
6208 }
6209
6210 static int getInvisibleActiveFromInvisibleElement(int element)
6211 {
6212   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6213           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6214           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6215           element);
6216 }
6217
6218 static int getInvisibleFromInvisibleActiveElement(int element)
6219 {
6220   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6221           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6222           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6223           element);
6224 }
6225
6226 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6227 {
6228   int x, y;
6229
6230   SCAN_PLAYFIELD(x, y)
6231   {
6232     int element = Tile[x][y];
6233
6234     if (element == EL_LIGHT_SWITCH &&
6235         game.light_time_left > 0)
6236     {
6237       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6238       TEST_DrawLevelField(x, y);
6239     }
6240     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6241              game.light_time_left == 0)
6242     {
6243       Tile[x][y] = EL_LIGHT_SWITCH;
6244       TEST_DrawLevelField(x, y);
6245     }
6246     else if (element == EL_EMC_DRIPPER &&
6247              game.light_time_left > 0)
6248     {
6249       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6250       TEST_DrawLevelField(x, y);
6251     }
6252     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6253              game.light_time_left == 0)
6254     {
6255       Tile[x][y] = EL_EMC_DRIPPER;
6256       TEST_DrawLevelField(x, y);
6257     }
6258     else if (element == EL_INVISIBLE_STEELWALL ||
6259              element == EL_INVISIBLE_WALL ||
6260              element == EL_INVISIBLE_SAND)
6261     {
6262       if (game.light_time_left > 0)
6263         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6264
6265       TEST_DrawLevelField(x, y);
6266
6267       // uncrumble neighbour fields, if needed
6268       if (element == EL_INVISIBLE_SAND)
6269         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6270     }
6271     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6272              element == EL_INVISIBLE_WALL_ACTIVE ||
6273              element == EL_INVISIBLE_SAND_ACTIVE)
6274     {
6275       if (game.light_time_left == 0)
6276         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6277
6278       TEST_DrawLevelField(x, y);
6279
6280       // re-crumble neighbour fields, if needed
6281       if (element == EL_INVISIBLE_SAND)
6282         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6283     }
6284   }
6285 }
6286
6287 static void RedrawAllInvisibleElementsForLenses(void)
6288 {
6289   int x, y;
6290
6291   SCAN_PLAYFIELD(x, y)
6292   {
6293     int element = Tile[x][y];
6294
6295     if (element == EL_EMC_DRIPPER &&
6296         game.lenses_time_left > 0)
6297     {
6298       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6299       TEST_DrawLevelField(x, y);
6300     }
6301     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6302              game.lenses_time_left == 0)
6303     {
6304       Tile[x][y] = EL_EMC_DRIPPER;
6305       TEST_DrawLevelField(x, y);
6306     }
6307     else if (element == EL_INVISIBLE_STEELWALL ||
6308              element == EL_INVISIBLE_WALL ||
6309              element == EL_INVISIBLE_SAND)
6310     {
6311       if (game.lenses_time_left > 0)
6312         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6313
6314       TEST_DrawLevelField(x, y);
6315
6316       // uncrumble neighbour fields, if needed
6317       if (element == EL_INVISIBLE_SAND)
6318         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6319     }
6320     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6321              element == EL_INVISIBLE_WALL_ACTIVE ||
6322              element == EL_INVISIBLE_SAND_ACTIVE)
6323     {
6324       if (game.lenses_time_left == 0)
6325         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6326
6327       TEST_DrawLevelField(x, y);
6328
6329       // re-crumble neighbour fields, if needed
6330       if (element == EL_INVISIBLE_SAND)
6331         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6332     }
6333   }
6334 }
6335
6336 static void RedrawAllInvisibleElementsForMagnifier(void)
6337 {
6338   int x, y;
6339
6340   SCAN_PLAYFIELD(x, y)
6341   {
6342     int element = Tile[x][y];
6343
6344     if (element == EL_EMC_FAKE_GRASS &&
6345         game.magnify_time_left > 0)
6346     {
6347       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6348       TEST_DrawLevelField(x, y);
6349     }
6350     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6351              game.magnify_time_left == 0)
6352     {
6353       Tile[x][y] = EL_EMC_FAKE_GRASS;
6354       TEST_DrawLevelField(x, y);
6355     }
6356     else if (IS_GATE_GRAY(element) &&
6357              game.magnify_time_left > 0)
6358     {
6359       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6360                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6361                     IS_EM_GATE_GRAY(element) ?
6362                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6363                     IS_EMC_GATE_GRAY(element) ?
6364                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6365                     IS_DC_GATE_GRAY(element) ?
6366                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6367                     element);
6368       TEST_DrawLevelField(x, y);
6369     }
6370     else if (IS_GATE_GRAY_ACTIVE(element) &&
6371              game.magnify_time_left == 0)
6372     {
6373       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6374                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6375                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6376                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6377                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6378                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6379                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6380                     EL_DC_GATE_WHITE_GRAY :
6381                     element);
6382       TEST_DrawLevelField(x, y);
6383     }
6384   }
6385 }
6386
6387 static void ToggleLightSwitch(int x, int y)
6388 {
6389   int element = Tile[x][y];
6390
6391   game.light_time_left =
6392     (element == EL_LIGHT_SWITCH ?
6393      level.time_light * FRAMES_PER_SECOND : 0);
6394
6395   RedrawAllLightSwitchesAndInvisibleElements();
6396 }
6397
6398 static void ActivateTimegateSwitch(int x, int y)
6399 {
6400   int xx, yy;
6401
6402   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6403
6404   SCAN_PLAYFIELD(xx, yy)
6405   {
6406     int element = Tile[xx][yy];
6407
6408     if (element == EL_TIMEGATE_CLOSED ||
6409         element == EL_TIMEGATE_CLOSING)
6410     {
6411       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6412       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6413     }
6414
6415     /*
6416     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6417     {
6418       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6419       TEST_DrawLevelField(xx, yy);
6420     }
6421     */
6422
6423   }
6424
6425   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6426                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6427 }
6428
6429 static void Impact(int x, int y)
6430 {
6431   boolean last_line = (y == lev_fieldy - 1);
6432   boolean object_hit = FALSE;
6433   boolean impact = (last_line || object_hit);
6434   int element = Tile[x][y];
6435   int smashed = EL_STEELWALL;
6436
6437   if (!last_line)       // check if element below was hit
6438   {
6439     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6440       return;
6441
6442     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6443                                          MovDir[x][y + 1] != MV_DOWN ||
6444                                          MovPos[x][y + 1] <= TILEY / 2));
6445
6446     // do not smash moving elements that left the smashed field in time
6447     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6448         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6449       object_hit = FALSE;
6450
6451 #if USE_QUICKSAND_IMPACT_BUGFIX
6452     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6453     {
6454       RemoveMovingField(x, y + 1);
6455       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6456       Tile[x][y + 2] = EL_ROCK;
6457       TEST_DrawLevelField(x, y + 2);
6458
6459       object_hit = TRUE;
6460     }
6461
6462     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6463     {
6464       RemoveMovingField(x, y + 1);
6465       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6466       Tile[x][y + 2] = EL_ROCK;
6467       TEST_DrawLevelField(x, y + 2);
6468
6469       object_hit = TRUE;
6470     }
6471 #endif
6472
6473     if (object_hit)
6474       smashed = MovingOrBlocked2Element(x, y + 1);
6475
6476     impact = (last_line || object_hit);
6477   }
6478
6479   if (!last_line && smashed == EL_ACID) // element falls into acid
6480   {
6481     SplashAcid(x, y + 1);
6482     return;
6483   }
6484
6485   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6486   // only reset graphic animation if graphic really changes after impact
6487   if (impact &&
6488       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6489   {
6490     ResetGfxAnimation(x, y);
6491     TEST_DrawLevelField(x, y);
6492   }
6493
6494   if (impact && CAN_EXPLODE_IMPACT(element))
6495   {
6496     Bang(x, y);
6497     return;
6498   }
6499   else if (impact && element == EL_PEARL &&
6500            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6501   {
6502     ResetGfxAnimation(x, y);
6503
6504     Tile[x][y] = EL_PEARL_BREAKING;
6505     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6506     return;
6507   }
6508   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6509   {
6510     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6511
6512     return;
6513   }
6514
6515   if (impact && element == EL_AMOEBA_DROP)
6516   {
6517     if (object_hit && IS_PLAYER(x, y + 1))
6518       KillPlayerUnlessEnemyProtected(x, y + 1);
6519     else if (object_hit && smashed == EL_PENGUIN)
6520       Bang(x, y + 1);
6521     else
6522     {
6523       Tile[x][y] = EL_AMOEBA_GROWING;
6524       Store[x][y] = EL_AMOEBA_WET;
6525
6526       ResetRandomAnimationValue(x, y);
6527     }
6528     return;
6529   }
6530
6531   if (object_hit)               // check which object was hit
6532   {
6533     if ((CAN_PASS_MAGIC_WALL(element) && 
6534          (smashed == EL_MAGIC_WALL ||
6535           smashed == EL_BD_MAGIC_WALL)) ||
6536         (CAN_PASS_DC_MAGIC_WALL(element) &&
6537          smashed == EL_DC_MAGIC_WALL))
6538     {
6539       int xx, yy;
6540       int activated_magic_wall =
6541         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6542          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6543          EL_DC_MAGIC_WALL_ACTIVE);
6544
6545       // activate magic wall / mill
6546       SCAN_PLAYFIELD(xx, yy)
6547       {
6548         if (Tile[xx][yy] == smashed)
6549           Tile[xx][yy] = activated_magic_wall;
6550       }
6551
6552       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6553       game.magic_wall_active = TRUE;
6554
6555       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6556                             SND_MAGIC_WALL_ACTIVATING :
6557                             smashed == EL_BD_MAGIC_WALL ?
6558                             SND_BD_MAGIC_WALL_ACTIVATING :
6559                             SND_DC_MAGIC_WALL_ACTIVATING));
6560     }
6561
6562     if (IS_PLAYER(x, y + 1))
6563     {
6564       if (CAN_SMASH_PLAYER(element))
6565       {
6566         KillPlayerUnlessEnemyProtected(x, y + 1);
6567         return;
6568       }
6569     }
6570     else if (smashed == EL_PENGUIN)
6571     {
6572       if (CAN_SMASH_PLAYER(element))
6573       {
6574         Bang(x, y + 1);
6575         return;
6576       }
6577     }
6578     else if (element == EL_BD_DIAMOND)
6579     {
6580       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6581       {
6582         Bang(x, y + 1);
6583         return;
6584       }
6585     }
6586     else if (((element == EL_SP_INFOTRON ||
6587                element == EL_SP_ZONK) &&
6588               (smashed == EL_SP_SNIKSNAK ||
6589                smashed == EL_SP_ELECTRON ||
6590                smashed == EL_SP_DISK_ORANGE)) ||
6591              (element == EL_SP_INFOTRON &&
6592               smashed == EL_SP_DISK_YELLOW))
6593     {
6594       Bang(x, y + 1);
6595       return;
6596     }
6597     else if (CAN_SMASH_EVERYTHING(element))
6598     {
6599       if (IS_CLASSIC_ENEMY(smashed) ||
6600           CAN_EXPLODE_SMASHED(smashed))
6601       {
6602         Bang(x, y + 1);
6603         return;
6604       }
6605       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6606       {
6607         if (smashed == EL_LAMP ||
6608             smashed == EL_LAMP_ACTIVE)
6609         {
6610           Bang(x, y + 1);
6611           return;
6612         }
6613         else if (smashed == EL_NUT)
6614         {
6615           Tile[x][y + 1] = EL_NUT_BREAKING;
6616           PlayLevelSound(x, y, SND_NUT_BREAKING);
6617           RaiseScoreElement(EL_NUT);
6618           return;
6619         }
6620         else if (smashed == EL_PEARL)
6621         {
6622           ResetGfxAnimation(x, y);
6623
6624           Tile[x][y + 1] = EL_PEARL_BREAKING;
6625           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6626           return;
6627         }
6628         else if (smashed == EL_DIAMOND)
6629         {
6630           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6631           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6632           return;
6633         }
6634         else if (IS_BELT_SWITCH(smashed))
6635         {
6636           ToggleBeltSwitch(x, y + 1);
6637         }
6638         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6639                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6640                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6641                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6642         {
6643           ToggleSwitchgateSwitch(x, y + 1);
6644         }
6645         else if (smashed == EL_LIGHT_SWITCH ||
6646                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6647         {
6648           ToggleLightSwitch(x, y + 1);
6649         }
6650         else
6651         {
6652           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6653
6654           CheckElementChangeBySide(x, y + 1, smashed, element,
6655                                    CE_SWITCHED, CH_SIDE_TOP);
6656           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6657                                             CH_SIDE_TOP);
6658         }
6659       }
6660       else
6661       {
6662         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6663       }
6664     }
6665   }
6666
6667   // play sound of magic wall / mill
6668   if (!last_line &&
6669       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6670        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6671        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6672   {
6673     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6674       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6675     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6676       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6677     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6678       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6679
6680     return;
6681   }
6682
6683   // play sound of object that hits the ground
6684   if (last_line || object_hit)
6685     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6686 }
6687
6688 static void TurnRoundExt(int x, int y)
6689 {
6690   static struct
6691   {
6692     int dx, dy;
6693   } move_xy[] =
6694   {
6695     {  0,  0 },
6696     { -1,  0 },
6697     { +1,  0 },
6698     {  0,  0 },
6699     {  0, -1 },
6700     {  0,  0 }, { 0, 0 }, { 0, 0 },
6701     {  0, +1 }
6702   };
6703   static struct
6704   {
6705     int left, right, back;
6706   } turn[] =
6707   {
6708     { 0,        0,              0        },
6709     { MV_DOWN,  MV_UP,          MV_RIGHT },
6710     { MV_UP,    MV_DOWN,        MV_LEFT  },
6711     { 0,        0,              0        },
6712     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6713     { 0,        0,              0        },
6714     { 0,        0,              0        },
6715     { 0,        0,              0        },
6716     { MV_RIGHT, MV_LEFT,        MV_UP    }
6717   };
6718
6719   int element = Tile[x][y];
6720   int move_pattern = element_info[element].move_pattern;
6721
6722   int old_move_dir = MovDir[x][y];
6723   int left_dir  = turn[old_move_dir].left;
6724   int right_dir = turn[old_move_dir].right;
6725   int back_dir  = turn[old_move_dir].back;
6726
6727   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6728   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6729   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6730   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6731
6732   int left_x  = x + left_dx,  left_y  = y + left_dy;
6733   int right_x = x + right_dx, right_y = y + right_dy;
6734   int move_x  = x + move_dx,  move_y  = y + move_dy;
6735
6736   int xx, yy;
6737
6738   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6739   {
6740     TestIfBadThingTouchesOtherBadThing(x, y);
6741
6742     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6743       MovDir[x][y] = right_dir;
6744     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6745       MovDir[x][y] = left_dir;
6746
6747     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6748       MovDelay[x][y] = 9;
6749     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6750       MovDelay[x][y] = 1;
6751   }
6752   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6753   {
6754     TestIfBadThingTouchesOtherBadThing(x, y);
6755
6756     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6757       MovDir[x][y] = left_dir;
6758     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6759       MovDir[x][y] = right_dir;
6760
6761     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6762       MovDelay[x][y] = 9;
6763     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6764       MovDelay[x][y] = 1;
6765   }
6766   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6767   {
6768     TestIfBadThingTouchesOtherBadThing(x, y);
6769
6770     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6771       MovDir[x][y] = left_dir;
6772     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6773       MovDir[x][y] = right_dir;
6774
6775     if (MovDir[x][y] != old_move_dir)
6776       MovDelay[x][y] = 9;
6777   }
6778   else if (element == EL_YAMYAM)
6779   {
6780     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6781     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6782
6783     if (can_turn_left && can_turn_right)
6784       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6785     else if (can_turn_left)
6786       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6787     else if (can_turn_right)
6788       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6789     else
6790       MovDir[x][y] = back_dir;
6791
6792     MovDelay[x][y] = 16 + 16 * RND(3);
6793   }
6794   else if (element == EL_DARK_YAMYAM)
6795   {
6796     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6797                                                          left_x, left_y);
6798     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6799                                                          right_x, right_y);
6800
6801     if (can_turn_left && can_turn_right)
6802       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6803     else if (can_turn_left)
6804       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6805     else if (can_turn_right)
6806       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6807     else
6808       MovDir[x][y] = back_dir;
6809
6810     MovDelay[x][y] = 16 + 16 * RND(3);
6811   }
6812   else if (element == EL_PACMAN)
6813   {
6814     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6815     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6816
6817     if (can_turn_left && can_turn_right)
6818       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6819     else if (can_turn_left)
6820       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6821     else if (can_turn_right)
6822       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6823     else
6824       MovDir[x][y] = back_dir;
6825
6826     MovDelay[x][y] = 6 + RND(40);
6827   }
6828   else if (element == EL_PIG)
6829   {
6830     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6831     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6832     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6833     boolean should_turn_left, should_turn_right, should_move_on;
6834     int rnd_value = 24;
6835     int rnd = RND(rnd_value);
6836
6837     should_turn_left = (can_turn_left &&
6838                         (!can_move_on ||
6839                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6840                                                    y + back_dy + left_dy)));
6841     should_turn_right = (can_turn_right &&
6842                          (!can_move_on ||
6843                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6844                                                     y + back_dy + right_dy)));
6845     should_move_on = (can_move_on &&
6846                       (!can_turn_left ||
6847                        !can_turn_right ||
6848                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6849                                                  y + move_dy + left_dy) ||
6850                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6851                                                  y + move_dy + right_dy)));
6852
6853     if (should_turn_left || should_turn_right || should_move_on)
6854     {
6855       if (should_turn_left && should_turn_right && should_move_on)
6856         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6857                         rnd < 2 * rnd_value / 3 ? right_dir :
6858                         old_move_dir);
6859       else if (should_turn_left && should_turn_right)
6860         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6861       else if (should_turn_left && should_move_on)
6862         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6863       else if (should_turn_right && should_move_on)
6864         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6865       else if (should_turn_left)
6866         MovDir[x][y] = left_dir;
6867       else if (should_turn_right)
6868         MovDir[x][y] = right_dir;
6869       else if (should_move_on)
6870         MovDir[x][y] = old_move_dir;
6871     }
6872     else if (can_move_on && rnd > rnd_value / 8)
6873       MovDir[x][y] = old_move_dir;
6874     else if (can_turn_left && can_turn_right)
6875       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6876     else if (can_turn_left && rnd > rnd_value / 8)
6877       MovDir[x][y] = left_dir;
6878     else if (can_turn_right && rnd > rnd_value/8)
6879       MovDir[x][y] = right_dir;
6880     else
6881       MovDir[x][y] = back_dir;
6882
6883     xx = x + move_xy[MovDir[x][y]].dx;
6884     yy = y + move_xy[MovDir[x][y]].dy;
6885
6886     if (!IN_LEV_FIELD(xx, yy) ||
6887         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
6888       MovDir[x][y] = old_move_dir;
6889
6890     MovDelay[x][y] = 0;
6891   }
6892   else if (element == EL_DRAGON)
6893   {
6894     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6895     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6896     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6897     int rnd_value = 24;
6898     int rnd = RND(rnd_value);
6899
6900     if (can_move_on && rnd > rnd_value / 8)
6901       MovDir[x][y] = old_move_dir;
6902     else if (can_turn_left && can_turn_right)
6903       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6904     else if (can_turn_left && rnd > rnd_value / 8)
6905       MovDir[x][y] = left_dir;
6906     else if (can_turn_right && rnd > rnd_value / 8)
6907       MovDir[x][y] = right_dir;
6908     else
6909       MovDir[x][y] = back_dir;
6910
6911     xx = x + move_xy[MovDir[x][y]].dx;
6912     yy = y + move_xy[MovDir[x][y]].dy;
6913
6914     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6915       MovDir[x][y] = old_move_dir;
6916
6917     MovDelay[x][y] = 0;
6918   }
6919   else if (element == EL_MOLE)
6920   {
6921     boolean can_move_on =
6922       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6923                             IS_AMOEBOID(Tile[move_x][move_y]) ||
6924                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
6925     if (!can_move_on)
6926     {
6927       boolean can_turn_left =
6928         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6929                               IS_AMOEBOID(Tile[left_x][left_y])));
6930
6931       boolean can_turn_right =
6932         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6933                               IS_AMOEBOID(Tile[right_x][right_y])));
6934
6935       if (can_turn_left && can_turn_right)
6936         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6937       else if (can_turn_left)
6938         MovDir[x][y] = left_dir;
6939       else
6940         MovDir[x][y] = right_dir;
6941     }
6942
6943     if (MovDir[x][y] != old_move_dir)
6944       MovDelay[x][y] = 9;
6945   }
6946   else if (element == EL_BALLOON)
6947   {
6948     MovDir[x][y] = game.wind_direction;
6949     MovDelay[x][y] = 0;
6950   }
6951   else if (element == EL_SPRING)
6952   {
6953     if (MovDir[x][y] & MV_HORIZONTAL)
6954     {
6955       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6956           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6957       {
6958         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6959         ResetGfxAnimation(move_x, move_y);
6960         TEST_DrawLevelField(move_x, move_y);
6961
6962         MovDir[x][y] = back_dir;
6963       }
6964       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6965                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6966         MovDir[x][y] = MV_NONE;
6967     }
6968
6969     MovDelay[x][y] = 0;
6970   }
6971   else if (element == EL_ROBOT ||
6972            element == EL_SATELLITE ||
6973            element == EL_PENGUIN ||
6974            element == EL_EMC_ANDROID)
6975   {
6976     int attr_x = -1, attr_y = -1;
6977
6978     if (game.all_players_gone)
6979     {
6980       attr_x = game.exit_x;
6981       attr_y = game.exit_y;
6982     }
6983     else
6984     {
6985       int i;
6986
6987       for (i = 0; i < MAX_PLAYERS; i++)
6988       {
6989         struct PlayerInfo *player = &stored_player[i];
6990         int jx = player->jx, jy = player->jy;
6991
6992         if (!player->active)
6993           continue;
6994
6995         if (attr_x == -1 ||
6996             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6997         {
6998           attr_x = jx;
6999           attr_y = jy;
7000         }
7001       }
7002     }
7003
7004     if (element == EL_ROBOT &&
7005         game.robot_wheel_x >= 0 &&
7006         game.robot_wheel_y >= 0 &&
7007         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7008          game.engine_version < VERSION_IDENT(3,1,0,0)))
7009     {
7010       attr_x = game.robot_wheel_x;
7011       attr_y = game.robot_wheel_y;
7012     }
7013
7014     if (element == EL_PENGUIN)
7015     {
7016       int i;
7017       static int xy[4][2] =
7018       {
7019         { 0, -1 },
7020         { -1, 0 },
7021         { +1, 0 },
7022         { 0, +1 }
7023       };
7024
7025       for (i = 0; i < NUM_DIRECTIONS; i++)
7026       {
7027         int ex = x + xy[i][0];
7028         int ey = y + xy[i][1];
7029
7030         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7031                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7032                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7033                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7034         {
7035           attr_x = ex;
7036           attr_y = ey;
7037           break;
7038         }
7039       }
7040     }
7041
7042     MovDir[x][y] = MV_NONE;
7043     if (attr_x < x)
7044       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7045     else if (attr_x > x)
7046       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7047     if (attr_y < y)
7048       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7049     else if (attr_y > y)
7050       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7051
7052     if (element == EL_ROBOT)
7053     {
7054       int newx, newy;
7055
7056       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7057         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7058       Moving2Blocked(x, y, &newx, &newy);
7059
7060       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7061         MovDelay[x][y] = 8 + 8 * !RND(3);
7062       else
7063         MovDelay[x][y] = 16;
7064     }
7065     else if (element == EL_PENGUIN)
7066     {
7067       int newx, newy;
7068
7069       MovDelay[x][y] = 1;
7070
7071       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7072       {
7073         boolean first_horiz = RND(2);
7074         int new_move_dir = MovDir[x][y];
7075
7076         MovDir[x][y] =
7077           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7078         Moving2Blocked(x, y, &newx, &newy);
7079
7080         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7081           return;
7082
7083         MovDir[x][y] =
7084           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7085         Moving2Blocked(x, y, &newx, &newy);
7086
7087         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7088           return;
7089
7090         MovDir[x][y] = old_move_dir;
7091         return;
7092       }
7093     }
7094     else if (element == EL_SATELLITE)
7095     {
7096       int newx, newy;
7097
7098       MovDelay[x][y] = 1;
7099
7100       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7101       {
7102         boolean first_horiz = RND(2);
7103         int new_move_dir = MovDir[x][y];
7104
7105         MovDir[x][y] =
7106           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7107         Moving2Blocked(x, y, &newx, &newy);
7108
7109         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7110           return;
7111
7112         MovDir[x][y] =
7113           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7114         Moving2Blocked(x, y, &newx, &newy);
7115
7116         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7117           return;
7118
7119         MovDir[x][y] = old_move_dir;
7120         return;
7121       }
7122     }
7123     else if (element == EL_EMC_ANDROID)
7124     {
7125       static int check_pos[16] =
7126       {
7127         -1,             //  0 => (invalid)
7128         7,              //  1 => MV_LEFT
7129         3,              //  2 => MV_RIGHT
7130         -1,             //  3 => (invalid)
7131         1,              //  4 =>            MV_UP
7132         0,              //  5 => MV_LEFT  | MV_UP
7133         2,              //  6 => MV_RIGHT | MV_UP
7134         -1,             //  7 => (invalid)
7135         5,              //  8 =>            MV_DOWN
7136         6,              //  9 => MV_LEFT  | MV_DOWN
7137         4,              // 10 => MV_RIGHT | MV_DOWN
7138         -1,             // 11 => (invalid)
7139         -1,             // 12 => (invalid)
7140         -1,             // 13 => (invalid)
7141         -1,             // 14 => (invalid)
7142         -1,             // 15 => (invalid)
7143       };
7144       static struct
7145       {
7146         int dx, dy;
7147         int dir;
7148       } check_xy[8] =
7149       {
7150         { -1, -1,       MV_LEFT  | MV_UP   },
7151         {  0, -1,                  MV_UP   },
7152         { +1, -1,       MV_RIGHT | MV_UP   },
7153         { +1,  0,       MV_RIGHT           },
7154         { +1, +1,       MV_RIGHT | MV_DOWN },
7155         {  0, +1,                  MV_DOWN },
7156         { -1, +1,       MV_LEFT  | MV_DOWN },
7157         { -1,  0,       MV_LEFT            },
7158       };
7159       int start_pos, check_order;
7160       boolean can_clone = FALSE;
7161       int i;
7162
7163       // check if there is any free field around current position
7164       for (i = 0; i < 8; i++)
7165       {
7166         int newx = x + check_xy[i].dx;
7167         int newy = y + check_xy[i].dy;
7168
7169         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7170         {
7171           can_clone = TRUE;
7172
7173           break;
7174         }
7175       }
7176
7177       if (can_clone)            // randomly find an element to clone
7178       {
7179         can_clone = FALSE;
7180
7181         start_pos = check_pos[RND(8)];
7182         check_order = (RND(2) ? -1 : +1);
7183
7184         for (i = 0; i < 8; i++)
7185         {
7186           int pos_raw = start_pos + i * check_order;
7187           int pos = (pos_raw + 8) % 8;
7188           int newx = x + check_xy[pos].dx;
7189           int newy = y + check_xy[pos].dy;
7190
7191           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7192           {
7193             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7194             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7195
7196             Store[x][y] = Tile[newx][newy];
7197
7198             can_clone = TRUE;
7199
7200             break;
7201           }
7202         }
7203       }
7204
7205       if (can_clone)            // randomly find a direction to move
7206       {
7207         can_clone = FALSE;
7208
7209         start_pos = check_pos[RND(8)];
7210         check_order = (RND(2) ? -1 : +1);
7211
7212         for (i = 0; i < 8; i++)
7213         {
7214           int pos_raw = start_pos + i * check_order;
7215           int pos = (pos_raw + 8) % 8;
7216           int newx = x + check_xy[pos].dx;
7217           int newy = y + check_xy[pos].dy;
7218           int new_move_dir = check_xy[pos].dir;
7219
7220           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7221           {
7222             MovDir[x][y] = new_move_dir;
7223             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7224
7225             can_clone = TRUE;
7226
7227             break;
7228           }
7229         }
7230       }
7231
7232       if (can_clone)            // cloning and moving successful
7233         return;
7234
7235       // cannot clone -- try to move towards player
7236
7237       start_pos = check_pos[MovDir[x][y] & 0x0f];
7238       check_order = (RND(2) ? -1 : +1);
7239
7240       for (i = 0; i < 3; i++)
7241       {
7242         // first check start_pos, then previous/next or (next/previous) pos
7243         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7244         int pos = (pos_raw + 8) % 8;
7245         int newx = x + check_xy[pos].dx;
7246         int newy = y + check_xy[pos].dy;
7247         int new_move_dir = check_xy[pos].dir;
7248
7249         if (IS_PLAYER(newx, newy))
7250           break;
7251
7252         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7253         {
7254           MovDir[x][y] = new_move_dir;
7255           MovDelay[x][y] = level.android_move_time * 8 + 1;
7256
7257           break;
7258         }
7259       }
7260     }
7261   }
7262   else if (move_pattern == MV_TURNING_LEFT ||
7263            move_pattern == MV_TURNING_RIGHT ||
7264            move_pattern == MV_TURNING_LEFT_RIGHT ||
7265            move_pattern == MV_TURNING_RIGHT_LEFT ||
7266            move_pattern == MV_TURNING_RANDOM ||
7267            move_pattern == MV_ALL_DIRECTIONS)
7268   {
7269     boolean can_turn_left =
7270       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7271     boolean can_turn_right =
7272       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7273
7274     if (element_info[element].move_stepsize == 0)       // "not moving"
7275       return;
7276
7277     if (move_pattern == MV_TURNING_LEFT)
7278       MovDir[x][y] = left_dir;
7279     else if (move_pattern == MV_TURNING_RIGHT)
7280       MovDir[x][y] = right_dir;
7281     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7282       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7283     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7284       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7285     else if (move_pattern == MV_TURNING_RANDOM)
7286       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7287                       can_turn_right && !can_turn_left ? right_dir :
7288                       RND(2) ? left_dir : right_dir);
7289     else if (can_turn_left && can_turn_right)
7290       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7291     else if (can_turn_left)
7292       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7293     else if (can_turn_right)
7294       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7295     else
7296       MovDir[x][y] = back_dir;
7297
7298     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7299   }
7300   else if (move_pattern == MV_HORIZONTAL ||
7301            move_pattern == MV_VERTICAL)
7302   {
7303     if (move_pattern & old_move_dir)
7304       MovDir[x][y] = back_dir;
7305     else if (move_pattern == MV_HORIZONTAL)
7306       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7307     else if (move_pattern == MV_VERTICAL)
7308       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7309
7310     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7311   }
7312   else if (move_pattern & MV_ANY_DIRECTION)
7313   {
7314     MovDir[x][y] = move_pattern;
7315     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7316   }
7317   else if (move_pattern & MV_WIND_DIRECTION)
7318   {
7319     MovDir[x][y] = game.wind_direction;
7320     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7321   }
7322   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7323   {
7324     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7325       MovDir[x][y] = left_dir;
7326     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7327       MovDir[x][y] = right_dir;
7328
7329     if (MovDir[x][y] != old_move_dir)
7330       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7331   }
7332   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7333   {
7334     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7335       MovDir[x][y] = right_dir;
7336     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7337       MovDir[x][y] = left_dir;
7338
7339     if (MovDir[x][y] != old_move_dir)
7340       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7341   }
7342   else if (move_pattern == MV_TOWARDS_PLAYER ||
7343            move_pattern == MV_AWAY_FROM_PLAYER)
7344   {
7345     int attr_x = -1, attr_y = -1;
7346     int newx, newy;
7347     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7348
7349     if (game.all_players_gone)
7350     {
7351       attr_x = game.exit_x;
7352       attr_y = game.exit_y;
7353     }
7354     else
7355     {
7356       int i;
7357
7358       for (i = 0; i < MAX_PLAYERS; i++)
7359       {
7360         struct PlayerInfo *player = &stored_player[i];
7361         int jx = player->jx, jy = player->jy;
7362
7363         if (!player->active)
7364           continue;
7365
7366         if (attr_x == -1 ||
7367             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7368         {
7369           attr_x = jx;
7370           attr_y = jy;
7371         }
7372       }
7373     }
7374
7375     MovDir[x][y] = MV_NONE;
7376     if (attr_x < x)
7377       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7378     else if (attr_x > x)
7379       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7380     if (attr_y < y)
7381       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7382     else if (attr_y > y)
7383       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7384
7385     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7386
7387     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7388     {
7389       boolean first_horiz = RND(2);
7390       int new_move_dir = MovDir[x][y];
7391
7392       if (element_info[element].move_stepsize == 0)     // "not moving"
7393       {
7394         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7395         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7396
7397         return;
7398       }
7399
7400       MovDir[x][y] =
7401         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7402       Moving2Blocked(x, y, &newx, &newy);
7403
7404       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7405         return;
7406
7407       MovDir[x][y] =
7408         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7409       Moving2Blocked(x, y, &newx, &newy);
7410
7411       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7412         return;
7413
7414       MovDir[x][y] = old_move_dir;
7415     }
7416   }
7417   else if (move_pattern == MV_WHEN_PUSHED ||
7418            move_pattern == MV_WHEN_DROPPED)
7419   {
7420     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7421       MovDir[x][y] = MV_NONE;
7422
7423     MovDelay[x][y] = 0;
7424   }
7425   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7426   {
7427     static int test_xy[7][2] =
7428     {
7429       { 0, -1 },
7430       { -1, 0 },
7431       { +1, 0 },
7432       { 0, +1 },
7433       { 0, -1 },
7434       { -1, 0 },
7435       { +1, 0 },
7436     };
7437     static int test_dir[7] =
7438     {
7439       MV_UP,
7440       MV_LEFT,
7441       MV_RIGHT,
7442       MV_DOWN,
7443       MV_UP,
7444       MV_LEFT,
7445       MV_RIGHT,
7446     };
7447     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7448     int move_preference = -1000000;     // start with very low preference
7449     int new_move_dir = MV_NONE;
7450     int start_test = RND(4);
7451     int i;
7452
7453     for (i = 0; i < NUM_DIRECTIONS; i++)
7454     {
7455       int move_dir = test_dir[start_test + i];
7456       int move_dir_preference;
7457
7458       xx = x + test_xy[start_test + i][0];
7459       yy = y + test_xy[start_test + i][1];
7460
7461       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7462           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7463       {
7464         new_move_dir = move_dir;
7465
7466         break;
7467       }
7468
7469       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7470         continue;
7471
7472       move_dir_preference = -1 * RunnerVisit[xx][yy];
7473       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7474         move_dir_preference = PlayerVisit[xx][yy];
7475
7476       if (move_dir_preference > move_preference)
7477       {
7478         // prefer field that has not been visited for the longest time
7479         move_preference = move_dir_preference;
7480         new_move_dir = move_dir;
7481       }
7482       else if (move_dir_preference == move_preference &&
7483                move_dir == old_move_dir)
7484       {
7485         // prefer last direction when all directions are preferred equally
7486         move_preference = move_dir_preference;
7487         new_move_dir = move_dir;
7488       }
7489     }
7490
7491     MovDir[x][y] = new_move_dir;
7492     if (old_move_dir != new_move_dir)
7493       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7494   }
7495 }
7496
7497 static void TurnRound(int x, int y)
7498 {
7499   int direction = MovDir[x][y];
7500
7501   TurnRoundExt(x, y);
7502
7503   GfxDir[x][y] = MovDir[x][y];
7504
7505   if (direction != MovDir[x][y])
7506     GfxFrame[x][y] = 0;
7507
7508   if (MovDelay[x][y])
7509     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7510
7511   ResetGfxFrame(x, y);
7512 }
7513
7514 static boolean JustBeingPushed(int x, int y)
7515 {
7516   int i;
7517
7518   for (i = 0; i < MAX_PLAYERS; i++)
7519   {
7520     struct PlayerInfo *player = &stored_player[i];
7521
7522     if (player->active && player->is_pushing && player->MovPos)
7523     {
7524       int next_jx = player->jx + (player->jx - player->last_jx);
7525       int next_jy = player->jy + (player->jy - player->last_jy);
7526
7527       if (x == next_jx && y == next_jy)
7528         return TRUE;
7529     }
7530   }
7531
7532   return FALSE;
7533 }
7534
7535 static void StartMoving(int x, int y)
7536 {
7537   boolean started_moving = FALSE;       // some elements can fall _and_ move
7538   int element = Tile[x][y];
7539
7540   if (Stop[x][y])
7541     return;
7542
7543   if (MovDelay[x][y] == 0)
7544     GfxAction[x][y] = ACTION_DEFAULT;
7545
7546   if (CAN_FALL(element) && y < lev_fieldy - 1)
7547   {
7548     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7549         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7550       if (JustBeingPushed(x, y))
7551         return;
7552
7553     if (element == EL_QUICKSAND_FULL)
7554     {
7555       if (IS_FREE(x, y + 1))
7556       {
7557         InitMovingField(x, y, MV_DOWN);
7558         started_moving = TRUE;
7559
7560         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7561 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7562         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7563           Store[x][y] = EL_ROCK;
7564 #else
7565         Store[x][y] = EL_ROCK;
7566 #endif
7567
7568         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7569       }
7570       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7571       {
7572         if (!MovDelay[x][y])
7573         {
7574           MovDelay[x][y] = TILEY + 1;
7575
7576           ResetGfxAnimation(x, y);
7577           ResetGfxAnimation(x, y + 1);
7578         }
7579
7580         if (MovDelay[x][y])
7581         {
7582           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7583           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7584
7585           MovDelay[x][y]--;
7586           if (MovDelay[x][y])
7587             return;
7588         }
7589
7590         Tile[x][y] = EL_QUICKSAND_EMPTY;
7591         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7592         Store[x][y + 1] = Store[x][y];
7593         Store[x][y] = 0;
7594
7595         PlayLevelSoundAction(x, y, ACTION_FILLING);
7596       }
7597       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7598       {
7599         if (!MovDelay[x][y])
7600         {
7601           MovDelay[x][y] = TILEY + 1;
7602
7603           ResetGfxAnimation(x, y);
7604           ResetGfxAnimation(x, y + 1);
7605         }
7606
7607         if (MovDelay[x][y])
7608         {
7609           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7610           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7611
7612           MovDelay[x][y]--;
7613           if (MovDelay[x][y])
7614             return;
7615         }
7616
7617         Tile[x][y] = EL_QUICKSAND_EMPTY;
7618         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7619         Store[x][y + 1] = Store[x][y];
7620         Store[x][y] = 0;
7621
7622         PlayLevelSoundAction(x, y, ACTION_FILLING);
7623       }
7624     }
7625     else if (element == EL_QUICKSAND_FAST_FULL)
7626     {
7627       if (IS_FREE(x, y + 1))
7628       {
7629         InitMovingField(x, y, MV_DOWN);
7630         started_moving = TRUE;
7631
7632         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7633 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7634         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7635           Store[x][y] = EL_ROCK;
7636 #else
7637         Store[x][y] = EL_ROCK;
7638 #endif
7639
7640         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7641       }
7642       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7643       {
7644         if (!MovDelay[x][y])
7645         {
7646           MovDelay[x][y] = TILEY + 1;
7647
7648           ResetGfxAnimation(x, y);
7649           ResetGfxAnimation(x, y + 1);
7650         }
7651
7652         if (MovDelay[x][y])
7653         {
7654           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7655           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7656
7657           MovDelay[x][y]--;
7658           if (MovDelay[x][y])
7659             return;
7660         }
7661
7662         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7663         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7664         Store[x][y + 1] = Store[x][y];
7665         Store[x][y] = 0;
7666
7667         PlayLevelSoundAction(x, y, ACTION_FILLING);
7668       }
7669       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7670       {
7671         if (!MovDelay[x][y])
7672         {
7673           MovDelay[x][y] = TILEY + 1;
7674
7675           ResetGfxAnimation(x, y);
7676           ResetGfxAnimation(x, y + 1);
7677         }
7678
7679         if (MovDelay[x][y])
7680         {
7681           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7682           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7683
7684           MovDelay[x][y]--;
7685           if (MovDelay[x][y])
7686             return;
7687         }
7688
7689         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7690         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7691         Store[x][y + 1] = Store[x][y];
7692         Store[x][y] = 0;
7693
7694         PlayLevelSoundAction(x, y, ACTION_FILLING);
7695       }
7696     }
7697     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7698              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7699     {
7700       InitMovingField(x, y, MV_DOWN);
7701       started_moving = TRUE;
7702
7703       Tile[x][y] = EL_QUICKSAND_FILLING;
7704       Store[x][y] = element;
7705
7706       PlayLevelSoundAction(x, y, ACTION_FILLING);
7707     }
7708     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7709              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7710     {
7711       InitMovingField(x, y, MV_DOWN);
7712       started_moving = TRUE;
7713
7714       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7715       Store[x][y] = element;
7716
7717       PlayLevelSoundAction(x, y, ACTION_FILLING);
7718     }
7719     else if (element == EL_MAGIC_WALL_FULL)
7720     {
7721       if (IS_FREE(x, y + 1))
7722       {
7723         InitMovingField(x, y, MV_DOWN);
7724         started_moving = TRUE;
7725
7726         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7727         Store[x][y] = EL_CHANGED(Store[x][y]);
7728       }
7729       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7730       {
7731         if (!MovDelay[x][y])
7732           MovDelay[x][y] = TILEY / 4 + 1;
7733
7734         if (MovDelay[x][y])
7735         {
7736           MovDelay[x][y]--;
7737           if (MovDelay[x][y])
7738             return;
7739         }
7740
7741         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7742         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7743         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7744         Store[x][y] = 0;
7745       }
7746     }
7747     else if (element == EL_BD_MAGIC_WALL_FULL)
7748     {
7749       if (IS_FREE(x, y + 1))
7750       {
7751         InitMovingField(x, y, MV_DOWN);
7752         started_moving = TRUE;
7753
7754         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7755         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7756       }
7757       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7758       {
7759         if (!MovDelay[x][y])
7760           MovDelay[x][y] = TILEY / 4 + 1;
7761
7762         if (MovDelay[x][y])
7763         {
7764           MovDelay[x][y]--;
7765           if (MovDelay[x][y])
7766             return;
7767         }
7768
7769         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7770         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7771         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7772         Store[x][y] = 0;
7773       }
7774     }
7775     else if (element == EL_DC_MAGIC_WALL_FULL)
7776     {
7777       if (IS_FREE(x, y + 1))
7778       {
7779         InitMovingField(x, y, MV_DOWN);
7780         started_moving = TRUE;
7781
7782         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7783         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7784       }
7785       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7786       {
7787         if (!MovDelay[x][y])
7788           MovDelay[x][y] = TILEY / 4 + 1;
7789
7790         if (MovDelay[x][y])
7791         {
7792           MovDelay[x][y]--;
7793           if (MovDelay[x][y])
7794             return;
7795         }
7796
7797         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7798         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7799         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7800         Store[x][y] = 0;
7801       }
7802     }
7803     else if ((CAN_PASS_MAGIC_WALL(element) &&
7804               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7805                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7806              (CAN_PASS_DC_MAGIC_WALL(element) &&
7807               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7808
7809     {
7810       InitMovingField(x, y, MV_DOWN);
7811       started_moving = TRUE;
7812
7813       Tile[x][y] =
7814         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7815          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7816          EL_DC_MAGIC_WALL_FILLING);
7817       Store[x][y] = element;
7818     }
7819     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
7820     {
7821       SplashAcid(x, y + 1);
7822
7823       InitMovingField(x, y, MV_DOWN);
7824       started_moving = TRUE;
7825
7826       Store[x][y] = EL_ACID;
7827     }
7828     else if (
7829              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7830               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7831              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7832               CAN_FALL(element) && WasJustFalling[x][y] &&
7833               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7834
7835              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7836               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7837               (Tile[x][y + 1] == EL_BLOCKED)))
7838     {
7839       /* this is needed for a special case not covered by calling "Impact()"
7840          from "ContinueMoving()": if an element moves to a tile directly below
7841          another element which was just falling on that tile (which was empty
7842          in the previous frame), the falling element above would just stop
7843          instead of smashing the element below (in previous version, the above
7844          element was just checked for "moving" instead of "falling", resulting
7845          in incorrect smashes caused by horizontal movement of the above
7846          element; also, the case of the player being the element to smash was
7847          simply not covered here... :-/ ) */
7848
7849       CheckCollision[x][y] = 0;
7850       CheckImpact[x][y] = 0;
7851
7852       Impact(x, y);
7853     }
7854     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7855     {
7856       if (MovDir[x][y] == MV_NONE)
7857       {
7858         InitMovingField(x, y, MV_DOWN);
7859         started_moving = TRUE;
7860       }
7861     }
7862     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
7863     {
7864       if (WasJustFalling[x][y]) // prevent animation from being restarted
7865         MovDir[x][y] = MV_DOWN;
7866
7867       InitMovingField(x, y, MV_DOWN);
7868       started_moving = TRUE;
7869     }
7870     else if (element == EL_AMOEBA_DROP)
7871     {
7872       Tile[x][y] = EL_AMOEBA_GROWING;
7873       Store[x][y] = EL_AMOEBA_WET;
7874     }
7875     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7876               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
7877              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7878              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7879     {
7880       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7881                                 (IS_FREE(x - 1, y + 1) ||
7882                                  Tile[x - 1][y + 1] == EL_ACID));
7883       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7884                                 (IS_FREE(x + 1, y + 1) ||
7885                                  Tile[x + 1][y + 1] == EL_ACID));
7886       boolean can_fall_any  = (can_fall_left || can_fall_right);
7887       boolean can_fall_both = (can_fall_left && can_fall_right);
7888       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
7889
7890       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7891       {
7892         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7893           can_fall_right = FALSE;
7894         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7895           can_fall_left = FALSE;
7896         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7897           can_fall_right = FALSE;
7898         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7899           can_fall_left = FALSE;
7900
7901         can_fall_any  = (can_fall_left || can_fall_right);
7902         can_fall_both = FALSE;
7903       }
7904
7905       if (can_fall_both)
7906       {
7907         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7908           can_fall_right = FALSE;       // slip down on left side
7909         else
7910           can_fall_left = !(can_fall_right = RND(2));
7911
7912         can_fall_both = FALSE;
7913       }
7914
7915       if (can_fall_any)
7916       {
7917         // if not determined otherwise, prefer left side for slipping down
7918         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7919         started_moving = TRUE;
7920       }
7921     }
7922     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
7923     {
7924       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7925       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7926       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
7927       int belt_dir = game.belt_dir[belt_nr];
7928
7929       if ((belt_dir == MV_LEFT  && left_is_free) ||
7930           (belt_dir == MV_RIGHT && right_is_free))
7931       {
7932         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7933
7934         InitMovingField(x, y, belt_dir);
7935         started_moving = TRUE;
7936
7937         Pushed[x][y] = TRUE;
7938         Pushed[nextx][y] = TRUE;
7939
7940         GfxAction[x][y] = ACTION_DEFAULT;
7941       }
7942       else
7943       {
7944         MovDir[x][y] = 0;       // if element was moving, stop it
7945       }
7946     }
7947   }
7948
7949   // not "else if" because of elements that can fall and move (EL_SPRING)
7950   if (CAN_MOVE(element) && !started_moving)
7951   {
7952     int move_pattern = element_info[element].move_pattern;
7953     int newx, newy;
7954
7955     Moving2Blocked(x, y, &newx, &newy);
7956
7957     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7958       return;
7959
7960     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7961         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7962     {
7963       WasJustMoving[x][y] = 0;
7964       CheckCollision[x][y] = 0;
7965
7966       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7967
7968       if (Tile[x][y] != element)        // element has changed
7969         return;
7970     }
7971
7972     if (!MovDelay[x][y])        // start new movement phase
7973     {
7974       // all objects that can change their move direction after each step
7975       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
7976
7977       if (element != EL_YAMYAM &&
7978           element != EL_DARK_YAMYAM &&
7979           element != EL_PACMAN &&
7980           !(move_pattern & MV_ANY_DIRECTION) &&
7981           move_pattern != MV_TURNING_LEFT &&
7982           move_pattern != MV_TURNING_RIGHT &&
7983           move_pattern != MV_TURNING_LEFT_RIGHT &&
7984           move_pattern != MV_TURNING_RIGHT_LEFT &&
7985           move_pattern != MV_TURNING_RANDOM)
7986       {
7987         TurnRound(x, y);
7988
7989         if (MovDelay[x][y] && (element == EL_BUG ||
7990                                element == EL_SPACESHIP ||
7991                                element == EL_SP_SNIKSNAK ||
7992                                element == EL_SP_ELECTRON ||
7993                                element == EL_MOLE))
7994           TEST_DrawLevelField(x, y);
7995       }
7996     }
7997
7998     if (MovDelay[x][y])         // wait some time before next movement
7999     {
8000       MovDelay[x][y]--;
8001
8002       if (element == EL_ROBOT ||
8003           element == EL_YAMYAM ||
8004           element == EL_DARK_YAMYAM)
8005       {
8006         DrawLevelElementAnimationIfNeeded(x, y, element);
8007         PlayLevelSoundAction(x, y, ACTION_WAITING);
8008       }
8009       else if (element == EL_SP_ELECTRON)
8010         DrawLevelElementAnimationIfNeeded(x, y, element);
8011       else if (element == EL_DRAGON)
8012       {
8013         int i;
8014         int dir = MovDir[x][y];
8015         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8016         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8017         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8018                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8019                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8020                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8021         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8022
8023         GfxAction[x][y] = ACTION_ATTACKING;
8024
8025         if (IS_PLAYER(x, y))
8026           DrawPlayerField(x, y);
8027         else
8028           TEST_DrawLevelField(x, y);
8029
8030         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8031
8032         for (i = 1; i <= 3; i++)
8033         {
8034           int xx = x + i * dx;
8035           int yy = y + i * dy;
8036           int sx = SCREENX(xx);
8037           int sy = SCREENY(yy);
8038           int flame_graphic = graphic + (i - 1);
8039
8040           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8041             break;
8042
8043           if (MovDelay[x][y])
8044           {
8045             int flamed = MovingOrBlocked2Element(xx, yy);
8046
8047             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8048               Bang(xx, yy);
8049             else
8050               RemoveMovingField(xx, yy);
8051
8052             ChangeDelay[xx][yy] = 0;
8053
8054             Tile[xx][yy] = EL_FLAMES;
8055
8056             if (IN_SCR_FIELD(sx, sy))
8057             {
8058               TEST_DrawLevelFieldCrumbled(xx, yy);
8059               DrawGraphic(sx, sy, flame_graphic, frame);
8060             }
8061           }
8062           else
8063           {
8064             if (Tile[xx][yy] == EL_FLAMES)
8065               Tile[xx][yy] = EL_EMPTY;
8066             TEST_DrawLevelField(xx, yy);
8067           }
8068         }
8069       }
8070
8071       if (MovDelay[x][y])       // element still has to wait some time
8072       {
8073         PlayLevelSoundAction(x, y, ACTION_WAITING);
8074
8075         return;
8076       }
8077     }
8078
8079     // now make next step
8080
8081     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8082
8083     if (DONT_COLLIDE_WITH(element) &&
8084         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8085         !PLAYER_ENEMY_PROTECTED(newx, newy))
8086     {
8087       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8088
8089       return;
8090     }
8091
8092     else if (CAN_MOVE_INTO_ACID(element) &&
8093              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8094              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8095              (MovDir[x][y] == MV_DOWN ||
8096               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8097     {
8098       SplashAcid(newx, newy);
8099       Store[x][y] = EL_ACID;
8100     }
8101     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8102     {
8103       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8104           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8105           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8106           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8107       {
8108         RemoveField(x, y);
8109         TEST_DrawLevelField(x, y);
8110
8111         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8112         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8113           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8114
8115         game.friends_still_needed--;
8116         if (!game.friends_still_needed &&
8117             !game.GameOver &&
8118             game.all_players_gone)
8119           LevelSolved();
8120
8121         return;
8122       }
8123       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8124       {
8125         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8126           TEST_DrawLevelField(newx, newy);
8127         else
8128           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8129       }
8130       else if (!IS_FREE(newx, newy))
8131       {
8132         GfxAction[x][y] = ACTION_WAITING;
8133
8134         if (IS_PLAYER(x, y))
8135           DrawPlayerField(x, y);
8136         else
8137           TEST_DrawLevelField(x, y);
8138
8139         return;
8140       }
8141     }
8142     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8143     {
8144       if (IS_FOOD_PIG(Tile[newx][newy]))
8145       {
8146         if (IS_MOVING(newx, newy))
8147           RemoveMovingField(newx, newy);
8148         else
8149         {
8150           Tile[newx][newy] = EL_EMPTY;
8151           TEST_DrawLevelField(newx, newy);
8152         }
8153
8154         PlayLevelSound(x, y, SND_PIG_DIGGING);
8155       }
8156       else if (!IS_FREE(newx, newy))
8157       {
8158         if (IS_PLAYER(x, y))
8159           DrawPlayerField(x, y);
8160         else
8161           TEST_DrawLevelField(x, y);
8162
8163         return;
8164       }
8165     }
8166     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8167     {
8168       if (Store[x][y] != EL_EMPTY)
8169       {
8170         boolean can_clone = FALSE;
8171         int xx, yy;
8172
8173         // check if element to clone is still there
8174         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8175         {
8176           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8177           {
8178             can_clone = TRUE;
8179
8180             break;
8181           }
8182         }
8183
8184         // cannot clone or target field not free anymore -- do not clone
8185         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8186           Store[x][y] = EL_EMPTY;
8187       }
8188
8189       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8190       {
8191         if (IS_MV_DIAGONAL(MovDir[x][y]))
8192         {
8193           int diagonal_move_dir = MovDir[x][y];
8194           int stored = Store[x][y];
8195           int change_delay = 8;
8196           int graphic;
8197
8198           // android is moving diagonally
8199
8200           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8201
8202           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8203           GfxElement[x][y] = EL_EMC_ANDROID;
8204           GfxAction[x][y] = ACTION_SHRINKING;
8205           GfxDir[x][y] = diagonal_move_dir;
8206           ChangeDelay[x][y] = change_delay;
8207
8208           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8209                                    GfxDir[x][y]);
8210
8211           DrawLevelGraphicAnimation(x, y, graphic);
8212           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8213
8214           if (Tile[newx][newy] == EL_ACID)
8215           {
8216             SplashAcid(newx, newy);
8217
8218             return;
8219           }
8220
8221           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8222
8223           Store[newx][newy] = EL_EMC_ANDROID;
8224           GfxElement[newx][newy] = EL_EMC_ANDROID;
8225           GfxAction[newx][newy] = ACTION_GROWING;
8226           GfxDir[newx][newy] = diagonal_move_dir;
8227           ChangeDelay[newx][newy] = change_delay;
8228
8229           graphic = el_act_dir2img(GfxElement[newx][newy],
8230                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8231
8232           DrawLevelGraphicAnimation(newx, newy, graphic);
8233           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8234
8235           return;
8236         }
8237         else
8238         {
8239           Tile[newx][newy] = EL_EMPTY;
8240           TEST_DrawLevelField(newx, newy);
8241
8242           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8243         }
8244       }
8245       else if (!IS_FREE(newx, newy))
8246       {
8247         return;
8248       }
8249     }
8250     else if (IS_CUSTOM_ELEMENT(element) &&
8251              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8252     {
8253       if (!DigFieldByCE(newx, newy, element))
8254         return;
8255
8256       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8257       {
8258         RunnerVisit[x][y] = FrameCounter;
8259         PlayerVisit[x][y] /= 8;         // expire player visit path
8260       }
8261     }
8262     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8263     {
8264       if (!IS_FREE(newx, newy))
8265       {
8266         if (IS_PLAYER(x, y))
8267           DrawPlayerField(x, y);
8268         else
8269           TEST_DrawLevelField(x, y);
8270
8271         return;
8272       }
8273       else
8274       {
8275         boolean wanna_flame = !RND(10);
8276         int dx = newx - x, dy = newy - y;
8277         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8278         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8279         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8280                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8281         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8282                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8283
8284         if ((wanna_flame ||
8285              IS_CLASSIC_ENEMY(element1) ||
8286              IS_CLASSIC_ENEMY(element2)) &&
8287             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8288             element1 != EL_FLAMES && element2 != EL_FLAMES)
8289         {
8290           ResetGfxAnimation(x, y);
8291           GfxAction[x][y] = ACTION_ATTACKING;
8292
8293           if (IS_PLAYER(x, y))
8294             DrawPlayerField(x, y);
8295           else
8296             TEST_DrawLevelField(x, y);
8297
8298           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8299
8300           MovDelay[x][y] = 50;
8301
8302           Tile[newx][newy] = EL_FLAMES;
8303           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8304             Tile[newx1][newy1] = EL_FLAMES;
8305           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8306             Tile[newx2][newy2] = EL_FLAMES;
8307
8308           return;
8309         }
8310       }
8311     }
8312     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8313              Tile[newx][newy] == EL_DIAMOND)
8314     {
8315       if (IS_MOVING(newx, newy))
8316         RemoveMovingField(newx, newy);
8317       else
8318       {
8319         Tile[newx][newy] = EL_EMPTY;
8320         TEST_DrawLevelField(newx, newy);
8321       }
8322
8323       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8324     }
8325     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8326              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8327     {
8328       if (AmoebaNr[newx][newy])
8329       {
8330         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8331         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8332             Tile[newx][newy] == EL_BD_AMOEBA)
8333           AmoebaCnt[AmoebaNr[newx][newy]]--;
8334       }
8335
8336       if (IS_MOVING(newx, newy))
8337       {
8338         RemoveMovingField(newx, newy);
8339       }
8340       else
8341       {
8342         Tile[newx][newy] = EL_EMPTY;
8343         TEST_DrawLevelField(newx, newy);
8344       }
8345
8346       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8347     }
8348     else if ((element == EL_PACMAN || element == EL_MOLE)
8349              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8350     {
8351       if (AmoebaNr[newx][newy])
8352       {
8353         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8354         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8355             Tile[newx][newy] == EL_BD_AMOEBA)
8356           AmoebaCnt[AmoebaNr[newx][newy]]--;
8357       }
8358
8359       if (element == EL_MOLE)
8360       {
8361         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8362         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8363
8364         ResetGfxAnimation(x, y);
8365         GfxAction[x][y] = ACTION_DIGGING;
8366         TEST_DrawLevelField(x, y);
8367
8368         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8369
8370         return;                         // wait for shrinking amoeba
8371       }
8372       else      // element == EL_PACMAN
8373       {
8374         Tile[newx][newy] = EL_EMPTY;
8375         TEST_DrawLevelField(newx, newy);
8376         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8377       }
8378     }
8379     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8380              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8381               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8382     {
8383       // wait for shrinking amoeba to completely disappear
8384       return;
8385     }
8386     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8387     {
8388       // object was running against a wall
8389
8390       TurnRound(x, y);
8391
8392       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8393         DrawLevelElementAnimation(x, y, element);
8394
8395       if (DONT_TOUCH(element))
8396         TestIfBadThingTouchesPlayer(x, y);
8397
8398       return;
8399     }
8400
8401     InitMovingField(x, y, MovDir[x][y]);
8402
8403     PlayLevelSoundAction(x, y, ACTION_MOVING);
8404   }
8405
8406   if (MovDir[x][y])
8407     ContinueMoving(x, y);
8408 }
8409
8410 void ContinueMoving(int x, int y)
8411 {
8412   int element = Tile[x][y];
8413   struct ElementInfo *ei = &element_info[element];
8414   int direction = MovDir[x][y];
8415   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8416   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8417   int newx = x + dx, newy = y + dy;
8418   int stored = Store[x][y];
8419   int stored_new = Store[newx][newy];
8420   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8421   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8422   boolean last_line = (newy == lev_fieldy - 1);
8423
8424   MovPos[x][y] += getElementMoveStepsize(x, y);
8425
8426   if (pushed_by_player) // special case: moving object pushed by player
8427     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8428
8429   if (ABS(MovPos[x][y]) < TILEX)
8430   {
8431     TEST_DrawLevelField(x, y);
8432
8433     return;     // element is still moving
8434   }
8435
8436   // element reached destination field
8437
8438   Tile[x][y] = EL_EMPTY;
8439   Tile[newx][newy] = element;
8440   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8441
8442   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8443   {
8444     element = Tile[newx][newy] = EL_ACID;
8445   }
8446   else if (element == EL_MOLE)
8447   {
8448     Tile[x][y] = EL_SAND;
8449
8450     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8451   }
8452   else if (element == EL_QUICKSAND_FILLING)
8453   {
8454     element = Tile[newx][newy] = get_next_element(element);
8455     Store[newx][newy] = Store[x][y];
8456   }
8457   else if (element == EL_QUICKSAND_EMPTYING)
8458   {
8459     Tile[x][y] = get_next_element(element);
8460     element = Tile[newx][newy] = Store[x][y];
8461   }
8462   else if (element == EL_QUICKSAND_FAST_FILLING)
8463   {
8464     element = Tile[newx][newy] = get_next_element(element);
8465     Store[newx][newy] = Store[x][y];
8466   }
8467   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8468   {
8469     Tile[x][y] = get_next_element(element);
8470     element = Tile[newx][newy] = Store[x][y];
8471   }
8472   else if (element == EL_MAGIC_WALL_FILLING)
8473   {
8474     element = Tile[newx][newy] = get_next_element(element);
8475     if (!game.magic_wall_active)
8476       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8477     Store[newx][newy] = Store[x][y];
8478   }
8479   else if (element == EL_MAGIC_WALL_EMPTYING)
8480   {
8481     Tile[x][y] = get_next_element(element);
8482     if (!game.magic_wall_active)
8483       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8484     element = Tile[newx][newy] = Store[x][y];
8485
8486     InitField(newx, newy, FALSE);
8487   }
8488   else if (element == EL_BD_MAGIC_WALL_FILLING)
8489   {
8490     element = Tile[newx][newy] = get_next_element(element);
8491     if (!game.magic_wall_active)
8492       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8493     Store[newx][newy] = Store[x][y];
8494   }
8495   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8496   {
8497     Tile[x][y] = get_next_element(element);
8498     if (!game.magic_wall_active)
8499       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8500     element = Tile[newx][newy] = Store[x][y];
8501
8502     InitField(newx, newy, FALSE);
8503   }
8504   else if (element == EL_DC_MAGIC_WALL_FILLING)
8505   {
8506     element = Tile[newx][newy] = get_next_element(element);
8507     if (!game.magic_wall_active)
8508       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8509     Store[newx][newy] = Store[x][y];
8510   }
8511   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8512   {
8513     Tile[x][y] = get_next_element(element);
8514     if (!game.magic_wall_active)
8515       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8516     element = Tile[newx][newy] = Store[x][y];
8517
8518     InitField(newx, newy, FALSE);
8519   }
8520   else if (element == EL_AMOEBA_DROPPING)
8521   {
8522     Tile[x][y] = get_next_element(element);
8523     element = Tile[newx][newy] = Store[x][y];
8524   }
8525   else if (element == EL_SOKOBAN_OBJECT)
8526   {
8527     if (Back[x][y])
8528       Tile[x][y] = Back[x][y];
8529
8530     if (Back[newx][newy])
8531       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8532
8533     Back[x][y] = Back[newx][newy] = 0;
8534   }
8535
8536   Store[x][y] = EL_EMPTY;
8537   MovPos[x][y] = 0;
8538   MovDir[x][y] = 0;
8539   MovDelay[x][y] = 0;
8540
8541   MovDelay[newx][newy] = 0;
8542
8543   if (CAN_CHANGE_OR_HAS_ACTION(element))
8544   {
8545     // copy element change control values to new field
8546     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8547     ChangePage[newx][newy]  = ChangePage[x][y];
8548     ChangeCount[newx][newy] = ChangeCount[x][y];
8549     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8550   }
8551
8552   CustomValue[newx][newy] = CustomValue[x][y];
8553
8554   ChangeDelay[x][y] = 0;
8555   ChangePage[x][y] = -1;
8556   ChangeCount[x][y] = 0;
8557   ChangeEvent[x][y] = -1;
8558
8559   CustomValue[x][y] = 0;
8560
8561   // copy animation control values to new field
8562   GfxFrame[newx][newy]  = GfxFrame[x][y];
8563   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8564   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8565   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8566
8567   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8568
8569   // some elements can leave other elements behind after moving
8570   if (ei->move_leave_element != EL_EMPTY &&
8571       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8572       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8573   {
8574     int move_leave_element = ei->move_leave_element;
8575
8576     // this makes it possible to leave the removed element again
8577     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8578       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8579
8580     Tile[x][y] = move_leave_element;
8581
8582     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8583       MovDir[x][y] = direction;
8584
8585     InitField(x, y, FALSE);
8586
8587     if (GFX_CRUMBLED(Tile[x][y]))
8588       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8589
8590     if (ELEM_IS_PLAYER(move_leave_element))
8591       RelocatePlayer(x, y, move_leave_element);
8592   }
8593
8594   // do this after checking for left-behind element
8595   ResetGfxAnimation(x, y);      // reset animation values for old field
8596
8597   if (!CAN_MOVE(element) ||
8598       (CAN_FALL(element) && direction == MV_DOWN &&
8599        (element == EL_SPRING ||
8600         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8601         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8602     GfxDir[x][y] = MovDir[newx][newy] = 0;
8603
8604   TEST_DrawLevelField(x, y);
8605   TEST_DrawLevelField(newx, newy);
8606
8607   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8608
8609   // prevent pushed element from moving on in pushed direction
8610   if (pushed_by_player && CAN_MOVE(element) &&
8611       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8612       !(element_info[element].move_pattern & direction))
8613     TurnRound(newx, newy);
8614
8615   // prevent elements on conveyor belt from moving on in last direction
8616   if (pushed_by_conveyor && CAN_FALL(element) &&
8617       direction & MV_HORIZONTAL)
8618     MovDir[newx][newy] = 0;
8619
8620   if (!pushed_by_player)
8621   {
8622     int nextx = newx + dx, nexty = newy + dy;
8623     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8624
8625     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8626
8627     if (CAN_FALL(element) && direction == MV_DOWN)
8628       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8629
8630     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8631       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8632
8633     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8634       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8635   }
8636
8637   if (DONT_TOUCH(element))      // object may be nasty to player or others
8638   {
8639     TestIfBadThingTouchesPlayer(newx, newy);
8640     TestIfBadThingTouchesFriend(newx, newy);
8641
8642     if (!IS_CUSTOM_ELEMENT(element))
8643       TestIfBadThingTouchesOtherBadThing(newx, newy);
8644   }
8645   else if (element == EL_PENGUIN)
8646     TestIfFriendTouchesBadThing(newx, newy);
8647
8648   if (DONT_GET_HIT_BY(element))
8649   {
8650     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8651   }
8652
8653   // give the player one last chance (one more frame) to move away
8654   if (CAN_FALL(element) && direction == MV_DOWN &&
8655       (last_line || (!IS_FREE(x, newy + 1) &&
8656                      (!IS_PLAYER(x, newy + 1) ||
8657                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8658     Impact(x, newy);
8659
8660   if (pushed_by_player && !game.use_change_when_pushing_bug)
8661   {
8662     int push_side = MV_DIR_OPPOSITE(direction);
8663     struct PlayerInfo *player = PLAYERINFO(x, y);
8664
8665     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8666                                player->index_bit, push_side);
8667     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8668                                         player->index_bit, push_side);
8669   }
8670
8671   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8672     MovDelay[newx][newy] = 1;
8673
8674   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8675
8676   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8677   TestIfElementHitsCustomElement(newx, newy, direction);
8678   TestIfPlayerTouchesCustomElement(newx, newy);
8679   TestIfElementTouchesCustomElement(newx, newy);
8680
8681   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8682       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8683     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8684                              MV_DIR_OPPOSITE(direction));
8685 }
8686
8687 int AmoebaNeighbourNr(int ax, int ay)
8688 {
8689   int i;
8690   int element = Tile[ax][ay];
8691   int group_nr = 0;
8692   static int xy[4][2] =
8693   {
8694     { 0, -1 },
8695     { -1, 0 },
8696     { +1, 0 },
8697     { 0, +1 }
8698   };
8699
8700   for (i = 0; i < NUM_DIRECTIONS; i++)
8701   {
8702     int x = ax + xy[i][0];
8703     int y = ay + xy[i][1];
8704
8705     if (!IN_LEV_FIELD(x, y))
8706       continue;
8707
8708     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8709       group_nr = AmoebaNr[x][y];
8710   }
8711
8712   return group_nr;
8713 }
8714
8715 static void AmoebaMerge(int ax, int ay)
8716 {
8717   int i, x, y, xx, yy;
8718   int new_group_nr = AmoebaNr[ax][ay];
8719   static int xy[4][2] =
8720   {
8721     { 0, -1 },
8722     { -1, 0 },
8723     { +1, 0 },
8724     { 0, +1 }
8725   };
8726
8727   if (new_group_nr == 0)
8728     return;
8729
8730   for (i = 0; i < NUM_DIRECTIONS; i++)
8731   {
8732     x = ax + xy[i][0];
8733     y = ay + xy[i][1];
8734
8735     if (!IN_LEV_FIELD(x, y))
8736       continue;
8737
8738     if ((Tile[x][y] == EL_AMOEBA_FULL ||
8739          Tile[x][y] == EL_BD_AMOEBA ||
8740          Tile[x][y] == EL_AMOEBA_DEAD) &&
8741         AmoebaNr[x][y] != new_group_nr)
8742     {
8743       int old_group_nr = AmoebaNr[x][y];
8744
8745       if (old_group_nr == 0)
8746         return;
8747
8748       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8749       AmoebaCnt[old_group_nr] = 0;
8750       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8751       AmoebaCnt2[old_group_nr] = 0;
8752
8753       SCAN_PLAYFIELD(xx, yy)
8754       {
8755         if (AmoebaNr[xx][yy] == old_group_nr)
8756           AmoebaNr[xx][yy] = new_group_nr;
8757       }
8758     }
8759   }
8760 }
8761
8762 void AmoebaToDiamond(int ax, int ay)
8763 {
8764   int i, x, y;
8765
8766   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
8767   {
8768     int group_nr = AmoebaNr[ax][ay];
8769
8770 #ifdef DEBUG
8771     if (group_nr == 0)
8772     {
8773       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
8774       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
8775
8776       return;
8777     }
8778 #endif
8779
8780     SCAN_PLAYFIELD(x, y)
8781     {
8782       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8783       {
8784         AmoebaNr[x][y] = 0;
8785         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
8786       }
8787     }
8788
8789     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8790                             SND_AMOEBA_TURNING_TO_GEM :
8791                             SND_AMOEBA_TURNING_TO_ROCK));
8792     Bang(ax, ay);
8793   }
8794   else
8795   {
8796     static int xy[4][2] =
8797     {
8798       { 0, -1 },
8799       { -1, 0 },
8800       { +1, 0 },
8801       { 0, +1 }
8802     };
8803
8804     for (i = 0; i < NUM_DIRECTIONS; i++)
8805     {
8806       x = ax + xy[i][0];
8807       y = ay + xy[i][1];
8808
8809       if (!IN_LEV_FIELD(x, y))
8810         continue;
8811
8812       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
8813       {
8814         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8815                               SND_AMOEBA_TURNING_TO_GEM :
8816                               SND_AMOEBA_TURNING_TO_ROCK));
8817         Bang(x, y);
8818       }
8819     }
8820   }
8821 }
8822
8823 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
8824 {
8825   int x, y;
8826   int group_nr = AmoebaNr[ax][ay];
8827   boolean done = FALSE;
8828
8829 #ifdef DEBUG
8830   if (group_nr == 0)
8831   {
8832     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
8833     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
8834
8835     return;
8836   }
8837 #endif
8838
8839   SCAN_PLAYFIELD(x, y)
8840   {
8841     if (AmoebaNr[x][y] == group_nr &&
8842         (Tile[x][y] == EL_AMOEBA_DEAD ||
8843          Tile[x][y] == EL_BD_AMOEBA ||
8844          Tile[x][y] == EL_AMOEBA_GROWING))
8845     {
8846       AmoebaNr[x][y] = 0;
8847       Tile[x][y] = new_element;
8848       InitField(x, y, FALSE);
8849       TEST_DrawLevelField(x, y);
8850       done = TRUE;
8851     }
8852   }
8853
8854   if (done)
8855     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8856                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8857                             SND_BD_AMOEBA_TURNING_TO_GEM));
8858 }
8859
8860 static void AmoebaGrowing(int x, int y)
8861 {
8862   static unsigned int sound_delay = 0;
8863   static unsigned int sound_delay_value = 0;
8864
8865   if (!MovDelay[x][y])          // start new growing cycle
8866   {
8867     MovDelay[x][y] = 7;
8868
8869     if (DelayReached(&sound_delay, sound_delay_value))
8870     {
8871       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8872       sound_delay_value = 30;
8873     }
8874   }
8875
8876   if (MovDelay[x][y])           // wait some time before growing bigger
8877   {
8878     MovDelay[x][y]--;
8879     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8880     {
8881       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8882                                            6 - MovDelay[x][y]);
8883
8884       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8885     }
8886
8887     if (!MovDelay[x][y])
8888     {
8889       Tile[x][y] = Store[x][y];
8890       Store[x][y] = 0;
8891       TEST_DrawLevelField(x, y);
8892     }
8893   }
8894 }
8895
8896 static void AmoebaShrinking(int x, int y)
8897 {
8898   static unsigned int sound_delay = 0;
8899   static unsigned int sound_delay_value = 0;
8900
8901   if (!MovDelay[x][y])          // start new shrinking cycle
8902   {
8903     MovDelay[x][y] = 7;
8904
8905     if (DelayReached(&sound_delay, sound_delay_value))
8906       sound_delay_value = 30;
8907   }
8908
8909   if (MovDelay[x][y])           // wait some time before shrinking
8910   {
8911     MovDelay[x][y]--;
8912     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8913     {
8914       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8915                                            6 - MovDelay[x][y]);
8916
8917       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8918     }
8919
8920     if (!MovDelay[x][y])
8921     {
8922       Tile[x][y] = EL_EMPTY;
8923       TEST_DrawLevelField(x, y);
8924
8925       // don't let mole enter this field in this cycle;
8926       // (give priority to objects falling to this field from above)
8927       Stop[x][y] = TRUE;
8928     }
8929   }
8930 }
8931
8932 static void AmoebaReproduce(int ax, int ay)
8933 {
8934   int i;
8935   int element = Tile[ax][ay];
8936   int graphic = el2img(element);
8937   int newax = ax, neway = ay;
8938   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8939   static int xy[4][2] =
8940   {
8941     { 0, -1 },
8942     { -1, 0 },
8943     { +1, 0 },
8944     { 0, +1 }
8945   };
8946
8947   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8948   {
8949     Tile[ax][ay] = EL_AMOEBA_DEAD;
8950     TEST_DrawLevelField(ax, ay);
8951     return;
8952   }
8953
8954   if (IS_ANIMATED(graphic))
8955     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8956
8957   if (!MovDelay[ax][ay])        // start making new amoeba field
8958     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8959
8960   if (MovDelay[ax][ay])         // wait some time before making new amoeba
8961   {
8962     MovDelay[ax][ay]--;
8963     if (MovDelay[ax][ay])
8964       return;
8965   }
8966
8967   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
8968   {
8969     int start = RND(4);
8970     int x = ax + xy[start][0];
8971     int y = ay + xy[start][1];
8972
8973     if (!IN_LEV_FIELD(x, y))
8974       return;
8975
8976     if (IS_FREE(x, y) ||
8977         CAN_GROW_INTO(Tile[x][y]) ||
8978         Tile[x][y] == EL_QUICKSAND_EMPTY ||
8979         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
8980     {
8981       newax = x;
8982       neway = y;
8983     }
8984
8985     if (newax == ax && neway == ay)
8986       return;
8987   }
8988   else                          // normal or "filled" (BD style) amoeba
8989   {
8990     int start = RND(4);
8991     boolean waiting_for_player = FALSE;
8992
8993     for (i = 0; i < NUM_DIRECTIONS; i++)
8994     {
8995       int j = (start + i) % 4;
8996       int x = ax + xy[j][0];
8997       int y = ay + xy[j][1];
8998
8999       if (!IN_LEV_FIELD(x, y))
9000         continue;
9001
9002       if (IS_FREE(x, y) ||
9003           CAN_GROW_INTO(Tile[x][y]) ||
9004           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9005           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9006       {
9007         newax = x;
9008         neway = y;
9009         break;
9010       }
9011       else if (IS_PLAYER(x, y))
9012         waiting_for_player = TRUE;
9013     }
9014
9015     if (newax == ax && neway == ay)             // amoeba cannot grow
9016     {
9017       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9018       {
9019         Tile[ax][ay] = EL_AMOEBA_DEAD;
9020         TEST_DrawLevelField(ax, ay);
9021         AmoebaCnt[AmoebaNr[ax][ay]]--;
9022
9023         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9024         {
9025           if (element == EL_AMOEBA_FULL)
9026             AmoebaToDiamond(ax, ay);
9027           else if (element == EL_BD_AMOEBA)
9028             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9029         }
9030       }
9031       return;
9032     }
9033     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9034     {
9035       // amoeba gets larger by growing in some direction
9036
9037       int new_group_nr = AmoebaNr[ax][ay];
9038
9039 #ifdef DEBUG
9040   if (new_group_nr == 0)
9041   {
9042     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9043           newax, neway);
9044     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9045
9046     return;
9047   }
9048 #endif
9049
9050       AmoebaNr[newax][neway] = new_group_nr;
9051       AmoebaCnt[new_group_nr]++;
9052       AmoebaCnt2[new_group_nr]++;
9053
9054       // if amoeba touches other amoeba(s) after growing, unify them
9055       AmoebaMerge(newax, neway);
9056
9057       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9058       {
9059         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9060         return;
9061       }
9062     }
9063   }
9064
9065   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9066       (neway == lev_fieldy - 1 && newax != ax))
9067   {
9068     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9069     Store[newax][neway] = element;
9070   }
9071   else if (neway == ay || element == EL_EMC_DRIPPER)
9072   {
9073     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9074
9075     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9076   }
9077   else
9078   {
9079     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9080     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9081     Store[ax][ay] = EL_AMOEBA_DROP;
9082     ContinueMoving(ax, ay);
9083     return;
9084   }
9085
9086   TEST_DrawLevelField(newax, neway);
9087 }
9088
9089 static void Life(int ax, int ay)
9090 {
9091   int x1, y1, x2, y2;
9092   int life_time = 40;
9093   int element = Tile[ax][ay];
9094   int graphic = el2img(element);
9095   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9096                          level.biomaze);
9097   boolean changed = FALSE;
9098
9099   if (IS_ANIMATED(graphic))
9100     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9101
9102   if (Stop[ax][ay])
9103     return;
9104
9105   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9106     MovDelay[ax][ay] = life_time;
9107
9108   if (MovDelay[ax][ay])         // wait some time before next cycle
9109   {
9110     MovDelay[ax][ay]--;
9111     if (MovDelay[ax][ay])
9112       return;
9113   }
9114
9115   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9116   {
9117     int xx = ax+x1, yy = ay+y1;
9118     int old_element = Tile[xx][yy];
9119     int num_neighbours = 0;
9120
9121     if (!IN_LEV_FIELD(xx, yy))
9122       continue;
9123
9124     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9125     {
9126       int x = xx+x2, y = yy+y2;
9127
9128       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9129         continue;
9130
9131       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9132       boolean is_neighbour = FALSE;
9133
9134       if (level.use_life_bugs)
9135         is_neighbour =
9136           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9137            (IS_FREE(x, y)                             &&  Stop[x][y]));
9138       else
9139         is_neighbour =
9140           (Last[x][y] == element || is_player_cell);
9141
9142       if (is_neighbour)
9143         num_neighbours++;
9144     }
9145
9146     boolean is_free = FALSE;
9147
9148     if (level.use_life_bugs)
9149       is_free = (IS_FREE(xx, yy));
9150     else
9151       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9152
9153     if (xx == ax && yy == ay)           // field in the middle
9154     {
9155       if (num_neighbours < life_parameter[0] ||
9156           num_neighbours > life_parameter[1])
9157       {
9158         Tile[xx][yy] = EL_EMPTY;
9159         if (Tile[xx][yy] != old_element)
9160           TEST_DrawLevelField(xx, yy);
9161         Stop[xx][yy] = TRUE;
9162         changed = TRUE;
9163       }
9164     }
9165     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9166     {                                   // free border field
9167       if (num_neighbours >= life_parameter[2] &&
9168           num_neighbours <= life_parameter[3])
9169       {
9170         Tile[xx][yy] = element;
9171         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9172         if (Tile[xx][yy] != old_element)
9173           TEST_DrawLevelField(xx, yy);
9174         Stop[xx][yy] = TRUE;
9175         changed = TRUE;
9176       }
9177     }
9178   }
9179
9180   if (changed)
9181     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9182                    SND_GAME_OF_LIFE_GROWING);
9183 }
9184
9185 static void InitRobotWheel(int x, int y)
9186 {
9187   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9188 }
9189
9190 static void RunRobotWheel(int x, int y)
9191 {
9192   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9193 }
9194
9195 static void StopRobotWheel(int x, int y)
9196 {
9197   if (game.robot_wheel_x == x &&
9198       game.robot_wheel_y == y)
9199   {
9200     game.robot_wheel_x = -1;
9201     game.robot_wheel_y = -1;
9202     game.robot_wheel_active = FALSE;
9203   }
9204 }
9205
9206 static void InitTimegateWheel(int x, int y)
9207 {
9208   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9209 }
9210
9211 static void RunTimegateWheel(int x, int y)
9212 {
9213   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9214 }
9215
9216 static void InitMagicBallDelay(int x, int y)
9217 {
9218   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9219 }
9220
9221 static void ActivateMagicBall(int bx, int by)
9222 {
9223   int x, y;
9224
9225   if (level.ball_random)
9226   {
9227     int pos_border = RND(8);    // select one of the eight border elements
9228     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9229     int xx = pos_content % 3;
9230     int yy = pos_content / 3;
9231
9232     x = bx - 1 + xx;
9233     y = by - 1 + yy;
9234
9235     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9236       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9237   }
9238   else
9239   {
9240     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9241     {
9242       int xx = x - bx + 1;
9243       int yy = y - by + 1;
9244
9245       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9246         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9247     }
9248   }
9249
9250   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9251 }
9252
9253 static void CheckExit(int x, int y)
9254 {
9255   if (game.gems_still_needed > 0 ||
9256       game.sokoban_fields_still_needed > 0 ||
9257       game.sokoban_objects_still_needed > 0 ||
9258       game.lights_still_needed > 0)
9259   {
9260     int element = Tile[x][y];
9261     int graphic = el2img(element);
9262
9263     if (IS_ANIMATED(graphic))
9264       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9265
9266     return;
9267   }
9268
9269   // do not re-open exit door closed after last player
9270   if (game.all_players_gone)
9271     return;
9272
9273   Tile[x][y] = EL_EXIT_OPENING;
9274
9275   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9276 }
9277
9278 static void CheckExitEM(int x, int y)
9279 {
9280   if (game.gems_still_needed > 0 ||
9281       game.sokoban_fields_still_needed > 0 ||
9282       game.sokoban_objects_still_needed > 0 ||
9283       game.lights_still_needed > 0)
9284   {
9285     int element = Tile[x][y];
9286     int graphic = el2img(element);
9287
9288     if (IS_ANIMATED(graphic))
9289       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9290
9291     return;
9292   }
9293
9294   // do not re-open exit door closed after last player
9295   if (game.all_players_gone)
9296     return;
9297
9298   Tile[x][y] = EL_EM_EXIT_OPENING;
9299
9300   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9301 }
9302
9303 static void CheckExitSteel(int x, int y)
9304 {
9305   if (game.gems_still_needed > 0 ||
9306       game.sokoban_fields_still_needed > 0 ||
9307       game.sokoban_objects_still_needed > 0 ||
9308       game.lights_still_needed > 0)
9309   {
9310     int element = Tile[x][y];
9311     int graphic = el2img(element);
9312
9313     if (IS_ANIMATED(graphic))
9314       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9315
9316     return;
9317   }
9318
9319   // do not re-open exit door closed after last player
9320   if (game.all_players_gone)
9321     return;
9322
9323   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9324
9325   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9326 }
9327
9328 static void CheckExitSteelEM(int x, int y)
9329 {
9330   if (game.gems_still_needed > 0 ||
9331       game.sokoban_fields_still_needed > 0 ||
9332       game.sokoban_objects_still_needed > 0 ||
9333       game.lights_still_needed > 0)
9334   {
9335     int element = Tile[x][y];
9336     int graphic = el2img(element);
9337
9338     if (IS_ANIMATED(graphic))
9339       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9340
9341     return;
9342   }
9343
9344   // do not re-open exit door closed after last player
9345   if (game.all_players_gone)
9346     return;
9347
9348   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9349
9350   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9351 }
9352
9353 static void CheckExitSP(int x, int y)
9354 {
9355   if (game.gems_still_needed > 0)
9356   {
9357     int element = Tile[x][y];
9358     int graphic = el2img(element);
9359
9360     if (IS_ANIMATED(graphic))
9361       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9362
9363     return;
9364   }
9365
9366   // do not re-open exit door closed after last player
9367   if (game.all_players_gone)
9368     return;
9369
9370   Tile[x][y] = EL_SP_EXIT_OPENING;
9371
9372   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9373 }
9374
9375 static void CloseAllOpenTimegates(void)
9376 {
9377   int x, y;
9378
9379   SCAN_PLAYFIELD(x, y)
9380   {
9381     int element = Tile[x][y];
9382
9383     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9384     {
9385       Tile[x][y] = EL_TIMEGATE_CLOSING;
9386
9387       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9388     }
9389   }
9390 }
9391
9392 static void DrawTwinkleOnField(int x, int y)
9393 {
9394   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9395     return;
9396
9397   if (Tile[x][y] == EL_BD_DIAMOND)
9398     return;
9399
9400   if (MovDelay[x][y] == 0)      // next animation frame
9401     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9402
9403   if (MovDelay[x][y] != 0)      // wait some time before next frame
9404   {
9405     MovDelay[x][y]--;
9406
9407     DrawLevelElementAnimation(x, y, Tile[x][y]);
9408
9409     if (MovDelay[x][y] != 0)
9410     {
9411       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9412                                            10 - MovDelay[x][y]);
9413
9414       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9415     }
9416   }
9417 }
9418
9419 static void MauerWaechst(int x, int y)
9420 {
9421   int delay = 6;
9422
9423   if (!MovDelay[x][y])          // next animation frame
9424     MovDelay[x][y] = 3 * delay;
9425
9426   if (MovDelay[x][y])           // wait some time before next frame
9427   {
9428     MovDelay[x][y]--;
9429
9430     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9431     {
9432       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9433       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9434
9435       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9436     }
9437
9438     if (!MovDelay[x][y])
9439     {
9440       if (MovDir[x][y] == MV_LEFT)
9441       {
9442         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9443           TEST_DrawLevelField(x - 1, y);
9444       }
9445       else if (MovDir[x][y] == MV_RIGHT)
9446       {
9447         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9448           TEST_DrawLevelField(x + 1, y);
9449       }
9450       else if (MovDir[x][y] == MV_UP)
9451       {
9452         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9453           TEST_DrawLevelField(x, y - 1);
9454       }
9455       else
9456       {
9457         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9458           TEST_DrawLevelField(x, y + 1);
9459       }
9460
9461       Tile[x][y] = Store[x][y];
9462       Store[x][y] = 0;
9463       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9464       TEST_DrawLevelField(x, y);
9465     }
9466   }
9467 }
9468
9469 static void MauerAbleger(int ax, int ay)
9470 {
9471   int element = Tile[ax][ay];
9472   int graphic = el2img(element);
9473   boolean oben_frei = FALSE, unten_frei = FALSE;
9474   boolean links_frei = FALSE, rechts_frei = FALSE;
9475   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9476   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9477   boolean new_wall = FALSE;
9478
9479   if (IS_ANIMATED(graphic))
9480     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9481
9482   if (!MovDelay[ax][ay])        // start building new wall
9483     MovDelay[ax][ay] = 6;
9484
9485   if (MovDelay[ax][ay])         // wait some time before building new wall
9486   {
9487     MovDelay[ax][ay]--;
9488     if (MovDelay[ax][ay])
9489       return;
9490   }
9491
9492   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9493     oben_frei = TRUE;
9494   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9495     unten_frei = TRUE;
9496   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9497     links_frei = TRUE;
9498   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9499     rechts_frei = TRUE;
9500
9501   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9502       element == EL_EXPANDABLE_WALL_ANY)
9503   {
9504     if (oben_frei)
9505     {
9506       Tile[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9507       Store[ax][ay-1] = element;
9508       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9509       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9510         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9511                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9512       new_wall = TRUE;
9513     }
9514     if (unten_frei)
9515     {
9516       Tile[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9517       Store[ax][ay+1] = element;
9518       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9519       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9520         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9521                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9522       new_wall = TRUE;
9523     }
9524   }
9525
9526   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9527       element == EL_EXPANDABLE_WALL_ANY ||
9528       element == EL_EXPANDABLE_WALL ||
9529       element == EL_BD_EXPANDABLE_WALL)
9530   {
9531     if (links_frei)
9532     {
9533       Tile[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9534       Store[ax-1][ay] = element;
9535       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9536       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9537         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9538                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9539       new_wall = TRUE;
9540     }
9541
9542     if (rechts_frei)
9543     {
9544       Tile[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9545       Store[ax+1][ay] = element;
9546       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9547       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9548         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9549                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9550       new_wall = TRUE;
9551     }
9552   }
9553
9554   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9555     TEST_DrawLevelField(ax, ay);
9556
9557   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9558     oben_massiv = TRUE;
9559   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9560     unten_massiv = TRUE;
9561   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9562     links_massiv = TRUE;
9563   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9564     rechts_massiv = TRUE;
9565
9566   if (((oben_massiv && unten_massiv) ||
9567        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9568        element == EL_EXPANDABLE_WALL) &&
9569       ((links_massiv && rechts_massiv) ||
9570        element == EL_EXPANDABLE_WALL_VERTICAL))
9571     Tile[ax][ay] = EL_WALL;
9572
9573   if (new_wall)
9574     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9575 }
9576
9577 static void MauerAblegerStahl(int ax, int ay)
9578 {
9579   int element = Tile[ax][ay];
9580   int graphic = el2img(element);
9581   boolean oben_frei = FALSE, unten_frei = FALSE;
9582   boolean links_frei = FALSE, rechts_frei = FALSE;
9583   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9584   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9585   boolean new_wall = FALSE;
9586
9587   if (IS_ANIMATED(graphic))
9588     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9589
9590   if (!MovDelay[ax][ay])        // start building new wall
9591     MovDelay[ax][ay] = 6;
9592
9593   if (MovDelay[ax][ay])         // wait some time before building new wall
9594   {
9595     MovDelay[ax][ay]--;
9596     if (MovDelay[ax][ay])
9597       return;
9598   }
9599
9600   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9601     oben_frei = TRUE;
9602   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9603     unten_frei = TRUE;
9604   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9605     links_frei = TRUE;
9606   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9607     rechts_frei = TRUE;
9608
9609   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9610       element == EL_EXPANDABLE_STEELWALL_ANY)
9611   {
9612     if (oben_frei)
9613     {
9614       Tile[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9615       Store[ax][ay-1] = element;
9616       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9617       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9618         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9619                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9620       new_wall = TRUE;
9621     }
9622     if (unten_frei)
9623     {
9624       Tile[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9625       Store[ax][ay+1] = element;
9626       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9627       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9628         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9629                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9630       new_wall = TRUE;
9631     }
9632   }
9633
9634   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9635       element == EL_EXPANDABLE_STEELWALL_ANY)
9636   {
9637     if (links_frei)
9638     {
9639       Tile[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9640       Store[ax-1][ay] = element;
9641       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9642       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9643         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9644                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9645       new_wall = TRUE;
9646     }
9647
9648     if (rechts_frei)
9649     {
9650       Tile[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9651       Store[ax+1][ay] = element;
9652       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9653       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9654         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9655                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9656       new_wall = TRUE;
9657     }
9658   }
9659
9660   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9661     oben_massiv = TRUE;
9662   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9663     unten_massiv = TRUE;
9664   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9665     links_massiv = TRUE;
9666   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9667     rechts_massiv = TRUE;
9668
9669   if (((oben_massiv && unten_massiv) ||
9670        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9671       ((links_massiv && rechts_massiv) ||
9672        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9673     Tile[ax][ay] = EL_STEELWALL;
9674
9675   if (new_wall)
9676     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9677 }
9678
9679 static void CheckForDragon(int x, int y)
9680 {
9681   int i, j;
9682   boolean dragon_found = FALSE;
9683   static int xy[4][2] =
9684   {
9685     { 0, -1 },
9686     { -1, 0 },
9687     { +1, 0 },
9688     { 0, +1 }
9689   };
9690
9691   for (i = 0; i < NUM_DIRECTIONS; i++)
9692   {
9693     for (j = 0; j < 4; j++)
9694     {
9695       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9696
9697       if (IN_LEV_FIELD(xx, yy) &&
9698           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9699       {
9700         if (Tile[xx][yy] == EL_DRAGON)
9701           dragon_found = TRUE;
9702       }
9703       else
9704         break;
9705     }
9706   }
9707
9708   if (!dragon_found)
9709   {
9710     for (i = 0; i < NUM_DIRECTIONS; i++)
9711     {
9712       for (j = 0; j < 3; j++)
9713       {
9714         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9715   
9716         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9717         {
9718           Tile[xx][yy] = EL_EMPTY;
9719           TEST_DrawLevelField(xx, yy);
9720         }
9721         else
9722           break;
9723       }
9724     }
9725   }
9726 }
9727
9728 static void InitBuggyBase(int x, int y)
9729 {
9730   int element = Tile[x][y];
9731   int activating_delay = FRAMES_PER_SECOND / 4;
9732
9733   ChangeDelay[x][y] =
9734     (element == EL_SP_BUGGY_BASE ?
9735      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9736      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9737      activating_delay :
9738      element == EL_SP_BUGGY_BASE_ACTIVE ?
9739      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9740 }
9741
9742 static void WarnBuggyBase(int x, int y)
9743 {
9744   int i;
9745   static int xy[4][2] =
9746   {
9747     { 0, -1 },
9748     { -1, 0 },
9749     { +1, 0 },
9750     { 0, +1 }
9751   };
9752
9753   for (i = 0; i < NUM_DIRECTIONS; i++)
9754   {
9755     int xx = x + xy[i][0];
9756     int yy = y + xy[i][1];
9757
9758     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9759     {
9760       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9761
9762       break;
9763     }
9764   }
9765 }
9766
9767 static void InitTrap(int x, int y)
9768 {
9769   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9770 }
9771
9772 static void ActivateTrap(int x, int y)
9773 {
9774   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9775 }
9776
9777 static void ChangeActiveTrap(int x, int y)
9778 {
9779   int graphic = IMG_TRAP_ACTIVE;
9780
9781   // if new animation frame was drawn, correct crumbled sand border
9782   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9783     TEST_DrawLevelFieldCrumbled(x, y);
9784 }
9785
9786 static int getSpecialActionElement(int element, int number, int base_element)
9787 {
9788   return (element != EL_EMPTY ? element :
9789           number != -1 ? base_element + number - 1 :
9790           EL_EMPTY);
9791 }
9792
9793 static int getModifiedActionNumber(int value_old, int operator, int operand,
9794                                    int value_min, int value_max)
9795 {
9796   int value_new = (operator == CA_MODE_SET      ? operand :
9797                    operator == CA_MODE_ADD      ? value_old + operand :
9798                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9799                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9800                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9801                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9802                    value_old);
9803
9804   return (value_new < value_min ? value_min :
9805           value_new > value_max ? value_max :
9806           value_new);
9807 }
9808
9809 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9810 {
9811   struct ElementInfo *ei = &element_info[element];
9812   struct ElementChangeInfo *change = &ei->change_page[page];
9813   int target_element = change->target_element;
9814   int action_type = change->action_type;
9815   int action_mode = change->action_mode;
9816   int action_arg = change->action_arg;
9817   int action_element = change->action_element;
9818   int i;
9819
9820   if (!change->has_action)
9821     return;
9822
9823   // ---------- determine action paramater values -----------------------------
9824
9825   int level_time_value =
9826     (level.time > 0 ? TimeLeft :
9827      TimePlayed);
9828
9829   int action_arg_element_raw =
9830     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9831      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9832      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9833      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9834      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9835      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9836      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9837      EL_EMPTY);
9838   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9839
9840   int action_arg_direction =
9841     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9842      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9843      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9844      change->actual_trigger_side :
9845      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9846      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9847      MV_NONE);
9848
9849   int action_arg_number_min =
9850     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9851      CA_ARG_MIN);
9852
9853   int action_arg_number_max =
9854     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9855      action_type == CA_SET_LEVEL_GEMS ? 999 :
9856      action_type == CA_SET_LEVEL_TIME ? 9999 :
9857      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9858      action_type == CA_SET_CE_VALUE ? 9999 :
9859      action_type == CA_SET_CE_SCORE ? 9999 :
9860      CA_ARG_MAX);
9861
9862   int action_arg_number_reset =
9863     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9864      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9865      action_type == CA_SET_LEVEL_TIME ? level.time :
9866      action_type == CA_SET_LEVEL_SCORE ? 0 :
9867      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9868      action_type == CA_SET_CE_SCORE ? 0 :
9869      0);
9870
9871   int action_arg_number =
9872     (action_arg <= CA_ARG_MAX ? action_arg :
9873      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9874      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9875      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9876      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9877      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9878      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9879      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9880      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9881      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9882      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9883      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
9884      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
9885      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9886      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9887      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9888      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9889      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9890      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9891      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9892      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9893      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9894      -1);
9895
9896   int action_arg_number_old =
9897     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
9898      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9899      action_type == CA_SET_LEVEL_SCORE ? game.score :
9900      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9901      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9902      0);
9903
9904   int action_arg_number_new =
9905     getModifiedActionNumber(action_arg_number_old,
9906                             action_mode, action_arg_number,
9907                             action_arg_number_min, action_arg_number_max);
9908
9909   int trigger_player_bits =
9910     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9911      change->actual_trigger_player_bits : change->trigger_player);
9912
9913   int action_arg_player_bits =
9914     (action_arg >= CA_ARG_PLAYER_1 &&
9915      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9916      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9917      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9918      PLAYER_BITS_ANY);
9919
9920   // ---------- execute action  -----------------------------------------------
9921
9922   switch (action_type)
9923   {
9924     case CA_NO_ACTION:
9925     {
9926       return;
9927     }
9928
9929     // ---------- level actions  ----------------------------------------------
9930
9931     case CA_RESTART_LEVEL:
9932     {
9933       game.restart_level = TRUE;
9934
9935       break;
9936     }
9937
9938     case CA_SHOW_ENVELOPE:
9939     {
9940       int element = getSpecialActionElement(action_arg_element,
9941                                             action_arg_number, EL_ENVELOPE_1);
9942
9943       if (IS_ENVELOPE(element))
9944         local_player->show_envelope = element;
9945
9946       break;
9947     }
9948
9949     case CA_SET_LEVEL_TIME:
9950     {
9951       if (level.time > 0)       // only modify limited time value
9952       {
9953         TimeLeft = action_arg_number_new;
9954
9955         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9956
9957         DisplayGameControlValues();
9958
9959         if (!TimeLeft && setup.time_limit)
9960           for (i = 0; i < MAX_PLAYERS; i++)
9961             KillPlayer(&stored_player[i]);
9962       }
9963
9964       break;
9965     }
9966
9967     case CA_SET_LEVEL_SCORE:
9968     {
9969       game.score = action_arg_number_new;
9970
9971       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
9972
9973       DisplayGameControlValues();
9974
9975       break;
9976     }
9977
9978     case CA_SET_LEVEL_GEMS:
9979     {
9980       game.gems_still_needed = action_arg_number_new;
9981
9982       game.snapshot.collected_item = TRUE;
9983
9984       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
9985
9986       DisplayGameControlValues();
9987
9988       break;
9989     }
9990
9991     case CA_SET_LEVEL_WIND:
9992     {
9993       game.wind_direction = action_arg_direction;
9994
9995       break;
9996     }
9997
9998     case CA_SET_LEVEL_RANDOM_SEED:
9999     {
10000       // ensure that setting a new random seed while playing is predictable
10001       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10002
10003       break;
10004     }
10005
10006     // ---------- player actions  ---------------------------------------------
10007
10008     case CA_MOVE_PLAYER:
10009     case CA_MOVE_PLAYER_NEW:
10010     {
10011       // automatically move to the next field in specified direction
10012       for (i = 0; i < MAX_PLAYERS; i++)
10013         if (trigger_player_bits & (1 << i))
10014           if (action_type == CA_MOVE_PLAYER ||
10015               stored_player[i].MovPos == 0)
10016             stored_player[i].programmed_action = action_arg_direction;
10017
10018       break;
10019     }
10020
10021     case CA_EXIT_PLAYER:
10022     {
10023       for (i = 0; i < MAX_PLAYERS; i++)
10024         if (action_arg_player_bits & (1 << i))
10025           ExitPlayer(&stored_player[i]);
10026
10027       if (game.players_still_needed == 0)
10028         LevelSolved();
10029
10030       break;
10031     }
10032
10033     case CA_KILL_PLAYER:
10034     {
10035       for (i = 0; i < MAX_PLAYERS; i++)
10036         if (action_arg_player_bits & (1 << i))
10037           KillPlayer(&stored_player[i]);
10038
10039       break;
10040     }
10041
10042     case CA_SET_PLAYER_KEYS:
10043     {
10044       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10045       int element = getSpecialActionElement(action_arg_element,
10046                                             action_arg_number, EL_KEY_1);
10047
10048       if (IS_KEY(element))
10049       {
10050         for (i = 0; i < MAX_PLAYERS; i++)
10051         {
10052           if (trigger_player_bits & (1 << i))
10053           {
10054             stored_player[i].key[KEY_NR(element)] = key_state;
10055
10056             DrawGameDoorValues();
10057           }
10058         }
10059       }
10060
10061       break;
10062     }
10063
10064     case CA_SET_PLAYER_SPEED:
10065     {
10066       for (i = 0; i < MAX_PLAYERS; i++)
10067       {
10068         if (trigger_player_bits & (1 << i))
10069         {
10070           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10071
10072           if (action_arg == CA_ARG_SPEED_FASTER &&
10073               stored_player[i].cannot_move)
10074           {
10075             action_arg_number = STEPSIZE_VERY_SLOW;
10076           }
10077           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10078                    action_arg == CA_ARG_SPEED_FASTER)
10079           {
10080             action_arg_number = 2;
10081             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10082                            CA_MODE_MULTIPLY);
10083           }
10084           else if (action_arg == CA_ARG_NUMBER_RESET)
10085           {
10086             action_arg_number = level.initial_player_stepsize[i];
10087           }
10088
10089           move_stepsize =
10090             getModifiedActionNumber(move_stepsize,
10091                                     action_mode,
10092                                     action_arg_number,
10093                                     action_arg_number_min,
10094                                     action_arg_number_max);
10095
10096           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10097         }
10098       }
10099
10100       break;
10101     }
10102
10103     case CA_SET_PLAYER_SHIELD:
10104     {
10105       for (i = 0; i < MAX_PLAYERS; i++)
10106       {
10107         if (trigger_player_bits & (1 << i))
10108         {
10109           if (action_arg == CA_ARG_SHIELD_OFF)
10110           {
10111             stored_player[i].shield_normal_time_left = 0;
10112             stored_player[i].shield_deadly_time_left = 0;
10113           }
10114           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10115           {
10116             stored_player[i].shield_normal_time_left = 999999;
10117           }
10118           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10119           {
10120             stored_player[i].shield_normal_time_left = 999999;
10121             stored_player[i].shield_deadly_time_left = 999999;
10122           }
10123         }
10124       }
10125
10126       break;
10127     }
10128
10129     case CA_SET_PLAYER_GRAVITY:
10130     {
10131       for (i = 0; i < MAX_PLAYERS; i++)
10132       {
10133         if (trigger_player_bits & (1 << i))
10134         {
10135           stored_player[i].gravity =
10136             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10137              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10138              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10139              stored_player[i].gravity);
10140         }
10141       }
10142
10143       break;
10144     }
10145
10146     case CA_SET_PLAYER_ARTWORK:
10147     {
10148       for (i = 0; i < MAX_PLAYERS; i++)
10149       {
10150         if (trigger_player_bits & (1 << i))
10151         {
10152           int artwork_element = action_arg_element;
10153
10154           if (action_arg == CA_ARG_ELEMENT_RESET)
10155             artwork_element =
10156               (level.use_artwork_element[i] ? level.artwork_element[i] :
10157                stored_player[i].element_nr);
10158
10159           if (stored_player[i].artwork_element != artwork_element)
10160             stored_player[i].Frame = 0;
10161
10162           stored_player[i].artwork_element = artwork_element;
10163
10164           SetPlayerWaiting(&stored_player[i], FALSE);
10165
10166           // set number of special actions for bored and sleeping animation
10167           stored_player[i].num_special_action_bored =
10168             get_num_special_action(artwork_element,
10169                                    ACTION_BORING_1, ACTION_BORING_LAST);
10170           stored_player[i].num_special_action_sleeping =
10171             get_num_special_action(artwork_element,
10172                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10173         }
10174       }
10175
10176       break;
10177     }
10178
10179     case CA_SET_PLAYER_INVENTORY:
10180     {
10181       for (i = 0; i < MAX_PLAYERS; i++)
10182       {
10183         struct PlayerInfo *player = &stored_player[i];
10184         int j, k;
10185
10186         if (trigger_player_bits & (1 << i))
10187         {
10188           int inventory_element = action_arg_element;
10189
10190           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10191               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10192               action_arg == CA_ARG_ELEMENT_ACTION)
10193           {
10194             int element = inventory_element;
10195             int collect_count = element_info[element].collect_count_initial;
10196
10197             if (!IS_CUSTOM_ELEMENT(element))
10198               collect_count = 1;
10199
10200             if (collect_count == 0)
10201               player->inventory_infinite_element = element;
10202             else
10203               for (k = 0; k < collect_count; k++)
10204                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10205                   player->inventory_element[player->inventory_size++] =
10206                     element;
10207           }
10208           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10209                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10210                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10211           {
10212             if (player->inventory_infinite_element != EL_UNDEFINED &&
10213                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10214                                      action_arg_element_raw))
10215               player->inventory_infinite_element = EL_UNDEFINED;
10216
10217             for (k = 0, j = 0; j < player->inventory_size; j++)
10218             {
10219               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10220                                         action_arg_element_raw))
10221                 player->inventory_element[k++] = player->inventory_element[j];
10222             }
10223
10224             player->inventory_size = k;
10225           }
10226           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10227           {
10228             if (player->inventory_size > 0)
10229             {
10230               for (j = 0; j < player->inventory_size - 1; j++)
10231                 player->inventory_element[j] = player->inventory_element[j + 1];
10232
10233               player->inventory_size--;
10234             }
10235           }
10236           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10237           {
10238             if (player->inventory_size > 0)
10239               player->inventory_size--;
10240           }
10241           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10242           {
10243             player->inventory_infinite_element = EL_UNDEFINED;
10244             player->inventory_size = 0;
10245           }
10246           else if (action_arg == CA_ARG_INVENTORY_RESET)
10247           {
10248             player->inventory_infinite_element = EL_UNDEFINED;
10249             player->inventory_size = 0;
10250
10251             if (level.use_initial_inventory[i])
10252             {
10253               for (j = 0; j < level.initial_inventory_size[i]; j++)
10254               {
10255                 int element = level.initial_inventory_content[i][j];
10256                 int collect_count = element_info[element].collect_count_initial;
10257
10258                 if (!IS_CUSTOM_ELEMENT(element))
10259                   collect_count = 1;
10260
10261                 if (collect_count == 0)
10262                   player->inventory_infinite_element = element;
10263                 else
10264                   for (k = 0; k < collect_count; k++)
10265                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10266                       player->inventory_element[player->inventory_size++] =
10267                         element;
10268               }
10269             }
10270           }
10271         }
10272       }
10273
10274       break;
10275     }
10276
10277     // ---------- CE actions  -------------------------------------------------
10278
10279     case CA_SET_CE_VALUE:
10280     {
10281       int last_ce_value = CustomValue[x][y];
10282
10283       CustomValue[x][y] = action_arg_number_new;
10284
10285       if (CustomValue[x][y] != last_ce_value)
10286       {
10287         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10288         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10289
10290         if (CustomValue[x][y] == 0)
10291         {
10292           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10293           ChangeCount[x][y] = 0;        // allow at least one more change
10294
10295           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10296           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10297         }
10298       }
10299
10300       break;
10301     }
10302
10303     case CA_SET_CE_SCORE:
10304     {
10305       int last_ce_score = ei->collect_score;
10306
10307       ei->collect_score = action_arg_number_new;
10308
10309       if (ei->collect_score != last_ce_score)
10310       {
10311         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10312         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10313
10314         if (ei->collect_score == 0)
10315         {
10316           int xx, yy;
10317
10318           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10319           ChangeCount[x][y] = 0;        // allow at least one more change
10320
10321           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10322           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10323
10324           /*
10325             This is a very special case that seems to be a mixture between
10326             CheckElementChange() and CheckTriggeredElementChange(): while
10327             the first one only affects single elements that are triggered
10328             directly, the second one affects multiple elements in the playfield
10329             that are triggered indirectly by another element. This is a third
10330             case: Changing the CE score always affects multiple identical CEs,
10331             so every affected CE must be checked, not only the single CE for
10332             which the CE score was changed in the first place (as every instance
10333             of that CE shares the same CE score, and therefore also can change)!
10334           */
10335           SCAN_PLAYFIELD(xx, yy)
10336           {
10337             if (Tile[xx][yy] == element)
10338               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10339                                  CE_SCORE_GETS_ZERO);
10340           }
10341         }
10342       }
10343
10344       break;
10345     }
10346
10347     case CA_SET_CE_ARTWORK:
10348     {
10349       int artwork_element = action_arg_element;
10350       boolean reset_frame = FALSE;
10351       int xx, yy;
10352
10353       if (action_arg == CA_ARG_ELEMENT_RESET)
10354         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10355                            element);
10356
10357       if (ei->gfx_element != artwork_element)
10358         reset_frame = TRUE;
10359
10360       ei->gfx_element = artwork_element;
10361
10362       SCAN_PLAYFIELD(xx, yy)
10363       {
10364         if (Tile[xx][yy] == element)
10365         {
10366           if (reset_frame)
10367           {
10368             ResetGfxAnimation(xx, yy);
10369             ResetRandomAnimationValue(xx, yy);
10370           }
10371
10372           TEST_DrawLevelField(xx, yy);
10373         }
10374       }
10375
10376       break;
10377     }
10378
10379     // ---------- engine actions  ---------------------------------------------
10380
10381     case CA_SET_ENGINE_SCAN_MODE:
10382     {
10383       InitPlayfieldScanMode(action_arg);
10384
10385       break;
10386     }
10387
10388     default:
10389       break;
10390   }
10391 }
10392
10393 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10394 {
10395   int old_element = Tile[x][y];
10396   int new_element = GetElementFromGroupElement(element);
10397   int previous_move_direction = MovDir[x][y];
10398   int last_ce_value = CustomValue[x][y];
10399   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10400   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10401   boolean add_player_onto_element = (new_element_is_player &&
10402                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10403                                      IS_WALKABLE(old_element));
10404
10405   if (!add_player_onto_element)
10406   {
10407     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10408       RemoveMovingField(x, y);
10409     else
10410       RemoveField(x, y);
10411
10412     Tile[x][y] = new_element;
10413
10414     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10415       MovDir[x][y] = previous_move_direction;
10416
10417     if (element_info[new_element].use_last_ce_value)
10418       CustomValue[x][y] = last_ce_value;
10419
10420     InitField_WithBug1(x, y, FALSE);
10421
10422     new_element = Tile[x][y];   // element may have changed
10423
10424     ResetGfxAnimation(x, y);
10425     ResetRandomAnimationValue(x, y);
10426
10427     TEST_DrawLevelField(x, y);
10428
10429     if (GFX_CRUMBLED(new_element))
10430       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10431   }
10432
10433   // check if element under the player changes from accessible to unaccessible
10434   // (needed for special case of dropping element which then changes)
10435   // (must be checked after creating new element for walkable group elements)
10436   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10437       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10438   {
10439     Bang(x, y);
10440
10441     return;
10442   }
10443
10444   // "ChangeCount" not set yet to allow "entered by player" change one time
10445   if (new_element_is_player)
10446     RelocatePlayer(x, y, new_element);
10447
10448   if (is_change)
10449     ChangeCount[x][y]++;        // count number of changes in the same frame
10450
10451   TestIfBadThingTouchesPlayer(x, y);
10452   TestIfPlayerTouchesCustomElement(x, y);
10453   TestIfElementTouchesCustomElement(x, y);
10454 }
10455
10456 static void CreateField(int x, int y, int element)
10457 {
10458   CreateFieldExt(x, y, element, FALSE);
10459 }
10460
10461 static void CreateElementFromChange(int x, int y, int element)
10462 {
10463   element = GET_VALID_RUNTIME_ELEMENT(element);
10464
10465   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10466   {
10467     int old_element = Tile[x][y];
10468
10469     // prevent changed element from moving in same engine frame
10470     // unless both old and new element can either fall or move
10471     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10472         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10473       Stop[x][y] = TRUE;
10474   }
10475
10476   CreateFieldExt(x, y, element, TRUE);
10477 }
10478
10479 static boolean ChangeElement(int x, int y, int element, int page)
10480 {
10481   struct ElementInfo *ei = &element_info[element];
10482   struct ElementChangeInfo *change = &ei->change_page[page];
10483   int ce_value = CustomValue[x][y];
10484   int ce_score = ei->collect_score;
10485   int target_element;
10486   int old_element = Tile[x][y];
10487
10488   // always use default change event to prevent running into a loop
10489   if (ChangeEvent[x][y] == -1)
10490     ChangeEvent[x][y] = CE_DELAY;
10491
10492   if (ChangeEvent[x][y] == CE_DELAY)
10493   {
10494     // reset actual trigger element, trigger player and action element
10495     change->actual_trigger_element = EL_EMPTY;
10496     change->actual_trigger_player = EL_EMPTY;
10497     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10498     change->actual_trigger_side = CH_SIDE_NONE;
10499     change->actual_trigger_ce_value = 0;
10500     change->actual_trigger_ce_score = 0;
10501   }
10502
10503   // do not change elements more than a specified maximum number of changes
10504   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10505     return FALSE;
10506
10507   ChangeCount[x][y]++;          // count number of changes in the same frame
10508
10509   if (change->explode)
10510   {
10511     Bang(x, y);
10512
10513     return TRUE;
10514   }
10515
10516   if (change->use_target_content)
10517   {
10518     boolean complete_replace = TRUE;
10519     boolean can_replace[3][3];
10520     int xx, yy;
10521
10522     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10523     {
10524       boolean is_empty;
10525       boolean is_walkable;
10526       boolean is_diggable;
10527       boolean is_collectible;
10528       boolean is_removable;
10529       boolean is_destructible;
10530       int ex = x + xx - 1;
10531       int ey = y + yy - 1;
10532       int content_element = change->target_content.e[xx][yy];
10533       int e;
10534
10535       can_replace[xx][yy] = TRUE;
10536
10537       if (ex == x && ey == y)   // do not check changing element itself
10538         continue;
10539
10540       if (content_element == EL_EMPTY_SPACE)
10541       {
10542         can_replace[xx][yy] = FALSE;    // do not replace border with space
10543
10544         continue;
10545       }
10546
10547       if (!IN_LEV_FIELD(ex, ey))
10548       {
10549         can_replace[xx][yy] = FALSE;
10550         complete_replace = FALSE;
10551
10552         continue;
10553       }
10554
10555       e = Tile[ex][ey];
10556
10557       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10558         e = MovingOrBlocked2Element(ex, ey);
10559
10560       is_empty = (IS_FREE(ex, ey) ||
10561                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10562
10563       is_walkable     = (is_empty || IS_WALKABLE(e));
10564       is_diggable     = (is_empty || IS_DIGGABLE(e));
10565       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10566       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10567       is_removable    = (is_diggable || is_collectible);
10568
10569       can_replace[xx][yy] =
10570         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10571           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10572           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10573           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10574           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10575           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10576          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10577
10578       if (!can_replace[xx][yy])
10579         complete_replace = FALSE;
10580     }
10581
10582     if (!change->only_if_complete || complete_replace)
10583     {
10584       boolean something_has_changed = FALSE;
10585
10586       if (change->only_if_complete && change->use_random_replace &&
10587           RND(100) < change->random_percentage)
10588         return FALSE;
10589
10590       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10591       {
10592         int ex = x + xx - 1;
10593         int ey = y + yy - 1;
10594         int content_element;
10595
10596         if (can_replace[xx][yy] && (!change->use_random_replace ||
10597                                     RND(100) < change->random_percentage))
10598         {
10599           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10600             RemoveMovingField(ex, ey);
10601
10602           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10603
10604           content_element = change->target_content.e[xx][yy];
10605           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10606                                               ce_value, ce_score);
10607
10608           CreateElementFromChange(ex, ey, target_element);
10609
10610           something_has_changed = TRUE;
10611
10612           // for symmetry reasons, freeze newly created border elements
10613           if (ex != x || ey != y)
10614             Stop[ex][ey] = TRUE;        // no more moving in this frame
10615         }
10616       }
10617
10618       if (something_has_changed)
10619       {
10620         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10621         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10622       }
10623     }
10624   }
10625   else
10626   {
10627     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10628                                         ce_value, ce_score);
10629
10630     if (element == EL_DIAGONAL_GROWING ||
10631         element == EL_DIAGONAL_SHRINKING)
10632     {
10633       target_element = Store[x][y];
10634
10635       Store[x][y] = EL_EMPTY;
10636     }
10637
10638     CreateElementFromChange(x, y, target_element);
10639
10640     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10641     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10642   }
10643
10644   // this uses direct change before indirect change
10645   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10646
10647   return TRUE;
10648 }
10649
10650 static void HandleElementChange(int x, int y, int page)
10651 {
10652   int element = MovingOrBlocked2Element(x, y);
10653   struct ElementInfo *ei = &element_info[element];
10654   struct ElementChangeInfo *change = &ei->change_page[page];
10655   boolean handle_action_before_change = FALSE;
10656
10657 #ifdef DEBUG
10658   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10659       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10660   {
10661     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10662           x, y, element, element_info[element].token_name);
10663     Debug("game:playing:HandleElementChange", "This should never happen!");
10664   }
10665 #endif
10666
10667   // this can happen with classic bombs on walkable, changing elements
10668   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10669   {
10670     return;
10671   }
10672
10673   if (ChangeDelay[x][y] == 0)           // initialize element change
10674   {
10675     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10676
10677     if (change->can_change)
10678     {
10679       // !!! not clear why graphic animation should be reset at all here !!!
10680       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10681       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10682
10683       /*
10684         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10685
10686         When using an animation frame delay of 1 (this only happens with
10687         "sp_zonk.moving.left/right" in the classic graphics), the default
10688         (non-moving) animation shows wrong animation frames (while the
10689         moving animation, like "sp_zonk.moving.left/right", is correct,
10690         so this graphical bug never shows up with the classic graphics).
10691         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10692         be drawn instead of the correct frames 0,1,2,3. This is caused by
10693         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10694         an element change: First when the change delay ("ChangeDelay[][]")
10695         counter has reached zero after decrementing, then a second time in
10696         the next frame (after "GfxFrame[][]" was already incremented) when
10697         "ChangeDelay[][]" is reset to the initial delay value again.
10698
10699         This causes frame 0 to be drawn twice, while the last frame won't
10700         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10701
10702         As some animations may already be cleverly designed around this bug
10703         (at least the "Snake Bite" snake tail animation does this), it cannot
10704         simply be fixed here without breaking such existing animations.
10705         Unfortunately, it cannot easily be detected if a graphics set was
10706         designed "before" or "after" the bug was fixed. As a workaround,
10707         a new graphics set option "game.graphics_engine_version" was added
10708         to be able to specify the game's major release version for which the
10709         graphics set was designed, which can then be used to decide if the
10710         bugfix should be used (version 4 and above) or not (version 3 or
10711         below, or if no version was specified at all, as with old sets).
10712
10713         (The wrong/fixed animation frames can be tested with the test level set
10714         "test_gfxframe" and level "000", which contains a specially prepared
10715         custom element at level position (x/y) == (11/9) which uses the zonk
10716         animation mentioned above. Using "game.graphics_engine_version: 4"
10717         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10718         This can also be seen from the debug output for this test element.)
10719       */
10720
10721       // when a custom element is about to change (for example by change delay),
10722       // do not reset graphic animation when the custom element is moving
10723       if (game.graphics_engine_version < 4 &&
10724           !IS_MOVING(x, y))
10725       {
10726         ResetGfxAnimation(x, y);
10727         ResetRandomAnimationValue(x, y);
10728       }
10729
10730       if (change->pre_change_function)
10731         change->pre_change_function(x, y);
10732     }
10733   }
10734
10735   ChangeDelay[x][y]--;
10736
10737   if (ChangeDelay[x][y] != 0)           // continue element change
10738   {
10739     if (change->can_change)
10740     {
10741       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10742
10743       if (IS_ANIMATED(graphic))
10744         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10745
10746       if (change->change_function)
10747         change->change_function(x, y);
10748     }
10749   }
10750   else                                  // finish element change
10751   {
10752     if (ChangePage[x][y] != -1)         // remember page from delayed change
10753     {
10754       page = ChangePage[x][y];
10755       ChangePage[x][y] = -1;
10756
10757       change = &ei->change_page[page];
10758     }
10759
10760     if (IS_MOVING(x, y))                // never change a running system ;-)
10761     {
10762       ChangeDelay[x][y] = 1;            // try change after next move step
10763       ChangePage[x][y] = page;          // remember page to use for change
10764
10765       return;
10766     }
10767
10768     // special case: set new level random seed before changing element
10769     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10770       handle_action_before_change = TRUE;
10771
10772     if (change->has_action && handle_action_before_change)
10773       ExecuteCustomElementAction(x, y, element, page);
10774
10775     if (change->can_change)
10776     {
10777       if (ChangeElement(x, y, element, page))
10778       {
10779         if (change->post_change_function)
10780           change->post_change_function(x, y);
10781       }
10782     }
10783
10784     if (change->has_action && !handle_action_before_change)
10785       ExecuteCustomElementAction(x, y, element, page);
10786   }
10787 }
10788
10789 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10790                                               int trigger_element,
10791                                               int trigger_event,
10792                                               int trigger_player,
10793                                               int trigger_side,
10794                                               int trigger_page)
10795 {
10796   boolean change_done_any = FALSE;
10797   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10798   int i;
10799
10800   if (!(trigger_events[trigger_element][trigger_event]))
10801     return FALSE;
10802
10803   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10804
10805   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10806   {
10807     int element = EL_CUSTOM_START + i;
10808     boolean change_done = FALSE;
10809     int p;
10810
10811     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10812         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10813       continue;
10814
10815     for (p = 0; p < element_info[element].num_change_pages; p++)
10816     {
10817       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10818
10819       if (change->can_change_or_has_action &&
10820           change->has_event[trigger_event] &&
10821           change->trigger_side & trigger_side &&
10822           change->trigger_player & trigger_player &&
10823           change->trigger_page & trigger_page_bits &&
10824           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10825       {
10826         change->actual_trigger_element = trigger_element;
10827         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10828         change->actual_trigger_player_bits = trigger_player;
10829         change->actual_trigger_side = trigger_side;
10830         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10831         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10832
10833         if ((change->can_change && !change_done) || change->has_action)
10834         {
10835           int x, y;
10836
10837           SCAN_PLAYFIELD(x, y)
10838           {
10839             if (Tile[x][y] == element)
10840             {
10841               if (change->can_change && !change_done)
10842               {
10843                 // if element already changed in this frame, not only prevent
10844                 // another element change (checked in ChangeElement()), but
10845                 // also prevent additional element actions for this element
10846
10847                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10848                     !level.use_action_after_change_bug)
10849                   continue;
10850
10851                 ChangeDelay[x][y] = 1;
10852                 ChangeEvent[x][y] = trigger_event;
10853
10854                 HandleElementChange(x, y, p);
10855               }
10856               else if (change->has_action)
10857               {
10858                 // if element already changed in this frame, not only prevent
10859                 // another element change (checked in ChangeElement()), but
10860                 // also prevent additional element actions for this element
10861
10862                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10863                     !level.use_action_after_change_bug)
10864                   continue;
10865
10866                 ExecuteCustomElementAction(x, y, element, p);
10867                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10868               }
10869             }
10870           }
10871
10872           if (change->can_change)
10873           {
10874             change_done = TRUE;
10875             change_done_any = TRUE;
10876           }
10877         }
10878       }
10879     }
10880   }
10881
10882   RECURSION_LOOP_DETECTION_END();
10883
10884   return change_done_any;
10885 }
10886
10887 static boolean CheckElementChangeExt(int x, int y,
10888                                      int element,
10889                                      int trigger_element,
10890                                      int trigger_event,
10891                                      int trigger_player,
10892                                      int trigger_side)
10893 {
10894   boolean change_done = FALSE;
10895   int p;
10896
10897   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10898       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10899     return FALSE;
10900
10901   if (Tile[x][y] == EL_BLOCKED)
10902   {
10903     Blocked2Moving(x, y, &x, &y);
10904     element = Tile[x][y];
10905   }
10906
10907   // check if element has already changed or is about to change after moving
10908   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10909        Tile[x][y] != element) ||
10910
10911       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10912        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10913         ChangePage[x][y] != -1)))
10914     return FALSE;
10915
10916   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10917
10918   for (p = 0; p < element_info[element].num_change_pages; p++)
10919   {
10920     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10921
10922     /* check trigger element for all events where the element that is checked
10923        for changing interacts with a directly adjacent element -- this is
10924        different to element changes that affect other elements to change on the
10925        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10926     boolean check_trigger_element =
10927       (trigger_event == CE_TOUCHING_X ||
10928        trigger_event == CE_HITTING_X ||
10929        trigger_event == CE_HIT_BY_X ||
10930        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
10931
10932     if (change->can_change_or_has_action &&
10933         change->has_event[trigger_event] &&
10934         change->trigger_side & trigger_side &&
10935         change->trigger_player & trigger_player &&
10936         (!check_trigger_element ||
10937          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10938     {
10939       change->actual_trigger_element = trigger_element;
10940       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10941       change->actual_trigger_player_bits = trigger_player;
10942       change->actual_trigger_side = trigger_side;
10943       change->actual_trigger_ce_value = CustomValue[x][y];
10944       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10945
10946       // special case: trigger element not at (x,y) position for some events
10947       if (check_trigger_element)
10948       {
10949         static struct
10950         {
10951           int dx, dy;
10952         } move_xy[] =
10953           {
10954             {  0,  0 },
10955             { -1,  0 },
10956             { +1,  0 },
10957             {  0,  0 },
10958             {  0, -1 },
10959             {  0,  0 }, { 0, 0 }, { 0, 0 },
10960             {  0, +1 }
10961           };
10962
10963         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10964         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10965
10966         change->actual_trigger_ce_value = CustomValue[xx][yy];
10967         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10968       }
10969
10970       if (change->can_change && !change_done)
10971       {
10972         ChangeDelay[x][y] = 1;
10973         ChangeEvent[x][y] = trigger_event;
10974
10975         HandleElementChange(x, y, p);
10976
10977         change_done = TRUE;
10978       }
10979       else if (change->has_action)
10980       {
10981         ExecuteCustomElementAction(x, y, element, p);
10982         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10983       }
10984     }
10985   }
10986
10987   RECURSION_LOOP_DETECTION_END();
10988
10989   return change_done;
10990 }
10991
10992 static void PlayPlayerSound(struct PlayerInfo *player)
10993 {
10994   int jx = player->jx, jy = player->jy;
10995   int sound_element = player->artwork_element;
10996   int last_action = player->last_action_waiting;
10997   int action = player->action_waiting;
10998
10999   if (player->is_waiting)
11000   {
11001     if (action != last_action)
11002       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11003     else
11004       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11005   }
11006   else
11007   {
11008     if (action != last_action)
11009       StopSound(element_info[sound_element].sound[last_action]);
11010
11011     if (last_action == ACTION_SLEEPING)
11012       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11013   }
11014 }
11015
11016 static void PlayAllPlayersSound(void)
11017 {
11018   int i;
11019
11020   for (i = 0; i < MAX_PLAYERS; i++)
11021     if (stored_player[i].active)
11022       PlayPlayerSound(&stored_player[i]);
11023 }
11024
11025 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11026 {
11027   boolean last_waiting = player->is_waiting;
11028   int move_dir = player->MovDir;
11029
11030   player->dir_waiting = move_dir;
11031   player->last_action_waiting = player->action_waiting;
11032
11033   if (is_waiting)
11034   {
11035     if (!last_waiting)          // not waiting -> waiting
11036     {
11037       player->is_waiting = TRUE;
11038
11039       player->frame_counter_bored =
11040         FrameCounter +
11041         game.player_boring_delay_fixed +
11042         GetSimpleRandom(game.player_boring_delay_random);
11043       player->frame_counter_sleeping =
11044         FrameCounter +
11045         game.player_sleeping_delay_fixed +
11046         GetSimpleRandom(game.player_sleeping_delay_random);
11047
11048       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11049     }
11050
11051     if (game.player_sleeping_delay_fixed +
11052         game.player_sleeping_delay_random > 0 &&
11053         player->anim_delay_counter == 0 &&
11054         player->post_delay_counter == 0 &&
11055         FrameCounter >= player->frame_counter_sleeping)
11056       player->is_sleeping = TRUE;
11057     else if (game.player_boring_delay_fixed +
11058              game.player_boring_delay_random > 0 &&
11059              FrameCounter >= player->frame_counter_bored)
11060       player->is_bored = TRUE;
11061
11062     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11063                               player->is_bored ? ACTION_BORING :
11064                               ACTION_WAITING);
11065
11066     if (player->is_sleeping && player->use_murphy)
11067     {
11068       // special case for sleeping Murphy when leaning against non-free tile
11069
11070       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11071           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11072            !IS_MOVING(player->jx - 1, player->jy)))
11073         move_dir = MV_LEFT;
11074       else 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_RIGHT;
11078       else
11079         player->is_sleeping = FALSE;
11080
11081       player->dir_waiting = move_dir;
11082     }
11083
11084     if (player->is_sleeping)
11085     {
11086       if (player->num_special_action_sleeping > 0)
11087       {
11088         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11089         {
11090           int last_special_action = player->special_action_sleeping;
11091           int num_special_action = player->num_special_action_sleeping;
11092           int special_action =
11093             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11094              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11095              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11096              last_special_action + 1 : ACTION_SLEEPING);
11097           int special_graphic =
11098             el_act_dir2img(player->artwork_element, special_action, move_dir);
11099
11100           player->anim_delay_counter =
11101             graphic_info[special_graphic].anim_delay_fixed +
11102             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11103           player->post_delay_counter =
11104             graphic_info[special_graphic].post_delay_fixed +
11105             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11106
11107           player->special_action_sleeping = special_action;
11108         }
11109
11110         if (player->anim_delay_counter > 0)
11111         {
11112           player->action_waiting = player->special_action_sleeping;
11113           player->anim_delay_counter--;
11114         }
11115         else if (player->post_delay_counter > 0)
11116         {
11117           player->post_delay_counter--;
11118         }
11119       }
11120     }
11121     else if (player->is_bored)
11122     {
11123       if (player->num_special_action_bored > 0)
11124       {
11125         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11126         {
11127           int special_action =
11128             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11129           int special_graphic =
11130             el_act_dir2img(player->artwork_element, special_action, move_dir);
11131
11132           player->anim_delay_counter =
11133             graphic_info[special_graphic].anim_delay_fixed +
11134             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11135           player->post_delay_counter =
11136             graphic_info[special_graphic].post_delay_fixed +
11137             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11138
11139           player->special_action_bored = special_action;
11140         }
11141
11142         if (player->anim_delay_counter > 0)
11143         {
11144           player->action_waiting = player->special_action_bored;
11145           player->anim_delay_counter--;
11146         }
11147         else if (player->post_delay_counter > 0)
11148         {
11149           player->post_delay_counter--;
11150         }
11151       }
11152     }
11153   }
11154   else if (last_waiting)        // waiting -> not waiting
11155   {
11156     player->is_waiting = FALSE;
11157     player->is_bored = FALSE;
11158     player->is_sleeping = FALSE;
11159
11160     player->frame_counter_bored = -1;
11161     player->frame_counter_sleeping = -1;
11162
11163     player->anim_delay_counter = 0;
11164     player->post_delay_counter = 0;
11165
11166     player->dir_waiting = player->MovDir;
11167     player->action_waiting = ACTION_DEFAULT;
11168
11169     player->special_action_bored = ACTION_DEFAULT;
11170     player->special_action_sleeping = ACTION_DEFAULT;
11171   }
11172 }
11173
11174 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11175 {
11176   if ((!player->is_moving  && player->was_moving) ||
11177       (player->MovPos == 0 && player->was_moving) ||
11178       (player->is_snapping && !player->was_snapping) ||
11179       (player->is_dropping && !player->was_dropping))
11180   {
11181     if (!CheckSaveEngineSnapshotToList())
11182       return;
11183
11184     player->was_moving = FALSE;
11185     player->was_snapping = TRUE;
11186     player->was_dropping = TRUE;
11187   }
11188   else
11189   {
11190     if (player->is_moving)
11191       player->was_moving = TRUE;
11192
11193     if (!player->is_snapping)
11194       player->was_snapping = FALSE;
11195
11196     if (!player->is_dropping)
11197       player->was_dropping = FALSE;
11198   }
11199 }
11200
11201 static void CheckSingleStepMode(struct PlayerInfo *player)
11202 {
11203   if (tape.single_step && tape.recording && !tape.pausing)
11204   {
11205     /* as it is called "single step mode", just return to pause mode when the
11206        player stopped moving after one tile (or never starts moving at all) */
11207     if (!player->is_moving &&
11208         !player->is_pushing &&
11209         !player->is_dropping_pressed)
11210       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11211   }
11212
11213   CheckSaveEngineSnapshot(player);
11214 }
11215
11216 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11217 {
11218   int left      = player_action & JOY_LEFT;
11219   int right     = player_action & JOY_RIGHT;
11220   int up        = player_action & JOY_UP;
11221   int down      = player_action & JOY_DOWN;
11222   int button1   = player_action & JOY_BUTTON_1;
11223   int button2   = player_action & JOY_BUTTON_2;
11224   int dx        = (left ? -1 : right ? 1 : 0);
11225   int dy        = (up   ? -1 : down  ? 1 : 0);
11226
11227   if (!player->active || tape.pausing)
11228     return 0;
11229
11230   if (player_action)
11231   {
11232     if (button1)
11233       SnapField(player, dx, dy);
11234     else
11235     {
11236       if (button2)
11237         DropElement(player);
11238
11239       MovePlayer(player, dx, dy);
11240     }
11241
11242     CheckSingleStepMode(player);
11243
11244     SetPlayerWaiting(player, FALSE);
11245
11246     return player_action;
11247   }
11248   else
11249   {
11250     // no actions for this player (no input at player's configured device)
11251
11252     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11253     SnapField(player, 0, 0);
11254     CheckGravityMovementWhenNotMoving(player);
11255
11256     if (player->MovPos == 0)
11257       SetPlayerWaiting(player, TRUE);
11258
11259     if (player->MovPos == 0)    // needed for tape.playing
11260       player->is_moving = FALSE;
11261
11262     player->is_dropping = FALSE;
11263     player->is_dropping_pressed = FALSE;
11264     player->drop_pressed_delay = 0;
11265
11266     CheckSingleStepMode(player);
11267
11268     return 0;
11269   }
11270 }
11271
11272 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11273                                          byte *tape_action)
11274 {
11275   if (!tape.use_mouse_actions)
11276     return;
11277
11278   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11279   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11280   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11281 }
11282
11283 static void SetTapeActionFromMouseAction(byte *tape_action,
11284                                          struct MouseActionInfo *mouse_action)
11285 {
11286   if (!tape.use_mouse_actions)
11287     return;
11288
11289   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11290   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11291   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11292 }
11293
11294 static void CheckLevelSolved(void)
11295 {
11296   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11297   {
11298     if (game_em.level_solved &&
11299         !game_em.game_over)                             // game won
11300     {
11301       LevelSolved();
11302
11303       game_em.game_over = TRUE;
11304
11305       game.all_players_gone = TRUE;
11306     }
11307
11308     if (game_em.game_over)                              // game lost
11309       game.all_players_gone = TRUE;
11310   }
11311   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11312   {
11313     if (game_sp.level_solved &&
11314         !game_sp.game_over)                             // game won
11315     {
11316       LevelSolved();
11317
11318       game_sp.game_over = TRUE;
11319
11320       game.all_players_gone = TRUE;
11321     }
11322
11323     if (game_sp.game_over)                              // game lost
11324       game.all_players_gone = TRUE;
11325   }
11326   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11327   {
11328     if (game_mm.level_solved &&
11329         !game_mm.game_over)                             // game won
11330     {
11331       LevelSolved();
11332
11333       game_mm.game_over = TRUE;
11334
11335       game.all_players_gone = TRUE;
11336     }
11337
11338     if (game_mm.game_over)                              // game lost
11339       game.all_players_gone = TRUE;
11340   }
11341 }
11342
11343 static void CheckLevelTime(void)
11344 {
11345   int i;
11346
11347   if (TimeFrames >= FRAMES_PER_SECOND)
11348   {
11349     TimeFrames = 0;
11350     TapeTime++;
11351
11352     for (i = 0; i < MAX_PLAYERS; i++)
11353     {
11354       struct PlayerInfo *player = &stored_player[i];
11355
11356       if (SHIELD_ON(player))
11357       {
11358         player->shield_normal_time_left--;
11359
11360         if (player->shield_deadly_time_left > 0)
11361           player->shield_deadly_time_left--;
11362       }
11363     }
11364
11365     if (!game.LevelSolved && !level.use_step_counter)
11366     {
11367       TimePlayed++;
11368
11369       if (TimeLeft > 0)
11370       {
11371         TimeLeft--;
11372
11373         if (TimeLeft <= 10 && setup.time_limit)
11374           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11375
11376         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11377            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11378
11379         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11380
11381         if (!TimeLeft && setup.time_limit)
11382         {
11383           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11384             game_em.lev->killed_out_of_time = TRUE;
11385           else
11386             for (i = 0; i < MAX_PLAYERS; i++)
11387               KillPlayer(&stored_player[i]);
11388         }
11389       }
11390       else if (game.no_time_limit && !game.all_players_gone)
11391       {
11392         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11393       }
11394
11395       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11396     }
11397
11398     if (tape.recording || tape.playing)
11399       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11400   }
11401
11402   if (tape.recording || tape.playing)
11403     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11404
11405   UpdateAndDisplayGameControlValues();
11406 }
11407
11408 void AdvanceFrameAndPlayerCounters(int player_nr)
11409 {
11410   int i;
11411
11412   // advance frame counters (global frame counter and time frame counter)
11413   FrameCounter++;
11414   TimeFrames++;
11415
11416   // advance player counters (counters for move delay, move animation etc.)
11417   for (i = 0; i < MAX_PLAYERS; i++)
11418   {
11419     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11420     int move_delay_value = stored_player[i].move_delay_value;
11421     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11422
11423     if (!advance_player_counters)       // not all players may be affected
11424       continue;
11425
11426     if (move_frames == 0)       // less than one move per game frame
11427     {
11428       int stepsize = TILEX / move_delay_value;
11429       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11430       int count = (stored_player[i].is_moving ?
11431                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11432
11433       if (count % delay == 0)
11434         move_frames = 1;
11435     }
11436
11437     stored_player[i].Frame += move_frames;
11438
11439     if (stored_player[i].MovPos != 0)
11440       stored_player[i].StepFrame += move_frames;
11441
11442     if (stored_player[i].move_delay > 0)
11443       stored_player[i].move_delay--;
11444
11445     // due to bugs in previous versions, counter must count up, not down
11446     if (stored_player[i].push_delay != -1)
11447       stored_player[i].push_delay++;
11448
11449     if (stored_player[i].drop_delay > 0)
11450       stored_player[i].drop_delay--;
11451
11452     if (stored_player[i].is_dropping_pressed)
11453       stored_player[i].drop_pressed_delay++;
11454   }
11455 }
11456
11457 void StartGameActions(boolean init_network_game, boolean record_tape,
11458                       int random_seed)
11459 {
11460   unsigned int new_random_seed = InitRND(random_seed);
11461
11462   if (record_tape)
11463     TapeStartRecording(new_random_seed);
11464
11465   if (init_network_game)
11466   {
11467     SendToServer_LevelFile();
11468     SendToServer_StartPlaying();
11469
11470     return;
11471   }
11472
11473   InitGame();
11474 }
11475
11476 static void GameActionsExt(void)
11477 {
11478 #if 0
11479   static unsigned int game_frame_delay = 0;
11480 #endif
11481   unsigned int game_frame_delay_value;
11482   byte *recorded_player_action;
11483   byte summarized_player_action = 0;
11484   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11485   int i;
11486
11487   // detect endless loops, caused by custom element programming
11488   if (recursion_loop_detected && recursion_loop_depth == 0)
11489   {
11490     char *message = getStringCat3("Internal Error! Element ",
11491                                   EL_NAME(recursion_loop_element),
11492                                   " caused endless loop! Quit the game?");
11493
11494     Warn("element '%s' caused endless loop in game engine",
11495          EL_NAME(recursion_loop_element));
11496
11497     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11498
11499     recursion_loop_detected = FALSE;    // if game should be continued
11500
11501     free(message);
11502
11503     return;
11504   }
11505
11506   if (game.restart_level)
11507     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11508
11509   CheckLevelSolved();
11510
11511   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11512     GameWon();
11513
11514   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11515     TapeStop();
11516
11517   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11518     return;
11519
11520   game_frame_delay_value =
11521     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11522
11523   if (tape.playing && tape.warp_forward && !tape.pausing)
11524     game_frame_delay_value = 0;
11525
11526   SetVideoFrameDelay(game_frame_delay_value);
11527
11528   // (de)activate virtual buttons depending on current game status
11529   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11530   {
11531     if (game.all_players_gone)  // if no players there to be controlled anymore
11532       SetOverlayActive(FALSE);
11533     else if (!tape.playing)     // if game continues after tape stopped playing
11534       SetOverlayActive(TRUE);
11535   }
11536
11537 #if 0
11538 #if 0
11539   // ---------- main game synchronization point ----------
11540
11541   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11542
11543   Debug("game:playing:skip", "skip == %d", skip);
11544
11545 #else
11546   // ---------- main game synchronization point ----------
11547
11548   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11549 #endif
11550 #endif
11551
11552   if (network_playing && !network_player_action_received)
11553   {
11554     // try to get network player actions in time
11555
11556     // last chance to get network player actions without main loop delay
11557     HandleNetworking();
11558
11559     // game was quit by network peer
11560     if (game_status != GAME_MODE_PLAYING)
11561       return;
11562
11563     // check if network player actions still missing and game still running
11564     if (!network_player_action_received && !checkGameEnded())
11565       return;           // failed to get network player actions in time
11566
11567     // do not yet reset "network_player_action_received" (for tape.pausing)
11568   }
11569
11570   if (tape.pausing)
11571     return;
11572
11573   // at this point we know that we really continue executing the game
11574
11575   network_player_action_received = FALSE;
11576
11577   // when playing tape, read previously recorded player input from tape data
11578   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11579
11580   local_player->effective_mouse_action = local_player->mouse_action;
11581
11582   if (recorded_player_action != NULL)
11583     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11584                                  recorded_player_action);
11585
11586   // TapePlayAction() may return NULL when toggling to "pause before death"
11587   if (tape.pausing)
11588     return;
11589
11590   if (tape.set_centered_player)
11591   {
11592     game.centered_player_nr_next = tape.centered_player_nr_next;
11593     game.set_centered_player = TRUE;
11594   }
11595
11596   for (i = 0; i < MAX_PLAYERS; i++)
11597   {
11598     summarized_player_action |= stored_player[i].action;
11599
11600     if (!network_playing && (game.team_mode || tape.playing))
11601       stored_player[i].effective_action = stored_player[i].action;
11602   }
11603
11604   if (network_playing && !checkGameEnded())
11605     SendToServer_MovePlayer(summarized_player_action);
11606
11607   // summarize all actions at local players mapped input device position
11608   // (this allows using different input devices in single player mode)
11609   if (!network.enabled && !game.team_mode)
11610     stored_player[map_player_action[local_player->index_nr]].effective_action =
11611       summarized_player_action;
11612
11613   // summarize all actions at centered player in local team mode
11614   if (tape.recording &&
11615       setup.team_mode && !network.enabled &&
11616       setup.input_on_focus &&
11617       game.centered_player_nr != -1)
11618   {
11619     for (i = 0; i < MAX_PLAYERS; i++)
11620       stored_player[map_player_action[i]].effective_action =
11621         (i == game.centered_player_nr ? summarized_player_action : 0);
11622   }
11623
11624   if (recorded_player_action != NULL)
11625     for (i = 0; i < MAX_PLAYERS; i++)
11626       stored_player[i].effective_action = recorded_player_action[i];
11627
11628   for (i = 0; i < MAX_PLAYERS; i++)
11629   {
11630     tape_action[i] = stored_player[i].effective_action;
11631
11632     /* (this may happen in the RND game engine if a player was not present on
11633        the playfield on level start, but appeared later from a custom element */
11634     if (setup.team_mode &&
11635         tape.recording &&
11636         tape_action[i] &&
11637         !tape.player_participates[i])
11638       tape.player_participates[i] = TRUE;
11639   }
11640
11641   SetTapeActionFromMouseAction(tape_action,
11642                                &local_player->effective_mouse_action);
11643
11644   // only record actions from input devices, but not programmed actions
11645   if (tape.recording)
11646     TapeRecordAction(tape_action);
11647
11648   // remember if game was played (especially after tape stopped playing)
11649   if (!tape.playing && summarized_player_action)
11650     game.GamePlayed = TRUE;
11651
11652 #if USE_NEW_PLAYER_ASSIGNMENTS
11653   // !!! also map player actions in single player mode !!!
11654   // if (game.team_mode)
11655   if (1)
11656   {
11657     byte mapped_action[MAX_PLAYERS];
11658
11659 #if DEBUG_PLAYER_ACTIONS
11660     printf(":::");
11661     for (i = 0; i < MAX_PLAYERS; i++)
11662       printf(" %d, ", stored_player[i].effective_action);
11663 #endif
11664
11665     for (i = 0; i < MAX_PLAYERS; i++)
11666       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11667
11668     for (i = 0; i < MAX_PLAYERS; i++)
11669       stored_player[i].effective_action = mapped_action[i];
11670
11671 #if DEBUG_PLAYER_ACTIONS
11672     printf(" =>");
11673     for (i = 0; i < MAX_PLAYERS; i++)
11674       printf(" %d, ", stored_player[i].effective_action);
11675     printf("\n");
11676 #endif
11677   }
11678 #if DEBUG_PLAYER_ACTIONS
11679   else
11680   {
11681     printf(":::");
11682     for (i = 0; i < MAX_PLAYERS; i++)
11683       printf(" %d, ", stored_player[i].effective_action);
11684     printf("\n");
11685   }
11686 #endif
11687 #endif
11688
11689   for (i = 0; i < MAX_PLAYERS; i++)
11690   {
11691     // allow engine snapshot in case of changed movement attempt
11692     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11693         (stored_player[i].effective_action & KEY_MOTION))
11694       game.snapshot.changed_action = TRUE;
11695
11696     // allow engine snapshot in case of snapping/dropping attempt
11697     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11698         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11699       game.snapshot.changed_action = TRUE;
11700
11701     game.snapshot.last_action[i] = stored_player[i].effective_action;
11702   }
11703
11704   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11705   {
11706     GameActions_EM_Main();
11707   }
11708   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11709   {
11710     GameActions_SP_Main();
11711   }
11712   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11713   {
11714     GameActions_MM_Main();
11715   }
11716   else
11717   {
11718     GameActions_RND_Main();
11719   }
11720
11721   BlitScreenToBitmap(backbuffer);
11722
11723   CheckLevelSolved();
11724   CheckLevelTime();
11725
11726   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11727
11728   if (global.show_frames_per_second)
11729   {
11730     static unsigned int fps_counter = 0;
11731     static int fps_frames = 0;
11732     unsigned int fps_delay_ms = Counter() - fps_counter;
11733
11734     fps_frames++;
11735
11736     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11737     {
11738       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11739
11740       fps_frames = 0;
11741       fps_counter = Counter();
11742
11743       // always draw FPS to screen after FPS value was updated
11744       redraw_mask |= REDRAW_FPS;
11745     }
11746
11747     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11748     if (GetDrawDeactivationMask() == REDRAW_NONE)
11749       redraw_mask |= REDRAW_FPS;
11750   }
11751 }
11752
11753 static void GameActions_CheckSaveEngineSnapshot(void)
11754 {
11755   if (!game.snapshot.save_snapshot)
11756     return;
11757
11758   // clear flag for saving snapshot _before_ saving snapshot
11759   game.snapshot.save_snapshot = FALSE;
11760
11761   SaveEngineSnapshotToList();
11762 }
11763
11764 void GameActions(void)
11765 {
11766   GameActionsExt();
11767
11768   GameActions_CheckSaveEngineSnapshot();
11769 }
11770
11771 void GameActions_EM_Main(void)
11772 {
11773   byte effective_action[MAX_PLAYERS];
11774   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11775   int i;
11776
11777   for (i = 0; i < MAX_PLAYERS; i++)
11778     effective_action[i] = stored_player[i].effective_action;
11779
11780   GameActions_EM(effective_action, warp_mode);
11781 }
11782
11783 void GameActions_SP_Main(void)
11784 {
11785   byte effective_action[MAX_PLAYERS];
11786   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11787   int i;
11788
11789   for (i = 0; i < MAX_PLAYERS; i++)
11790     effective_action[i] = stored_player[i].effective_action;
11791
11792   GameActions_SP(effective_action, warp_mode);
11793
11794   for (i = 0; i < MAX_PLAYERS; i++)
11795   {
11796     if (stored_player[i].force_dropping)
11797       stored_player[i].action |= KEY_BUTTON_DROP;
11798
11799     stored_player[i].force_dropping = FALSE;
11800   }
11801 }
11802
11803 void GameActions_MM_Main(void)
11804 {
11805   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11806
11807   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11808 }
11809
11810 void GameActions_RND_Main(void)
11811 {
11812   GameActions_RND();
11813 }
11814
11815 void GameActions_RND(void)
11816 {
11817   static struct MouseActionInfo mouse_action_last = { 0 };
11818   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
11819   int magic_wall_x = 0, magic_wall_y = 0;
11820   int i, x, y, element, graphic, last_gfx_frame;
11821
11822   InitPlayfieldScanModeVars();
11823
11824   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11825   {
11826     SCAN_PLAYFIELD(x, y)
11827     {
11828       ChangeCount[x][y] = 0;
11829       ChangeEvent[x][y] = -1;
11830     }
11831   }
11832
11833   if (game.set_centered_player)
11834   {
11835     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11836
11837     // switching to "all players" only possible if all players fit to screen
11838     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11839     {
11840       game.centered_player_nr_next = game.centered_player_nr;
11841       game.set_centered_player = FALSE;
11842     }
11843
11844     // do not switch focus to non-existing (or non-active) player
11845     if (game.centered_player_nr_next >= 0 &&
11846         !stored_player[game.centered_player_nr_next].active)
11847     {
11848       game.centered_player_nr_next = game.centered_player_nr;
11849       game.set_centered_player = FALSE;
11850     }
11851   }
11852
11853   if (game.set_centered_player &&
11854       ScreenMovPos == 0)        // screen currently aligned at tile position
11855   {
11856     int sx, sy;
11857
11858     if (game.centered_player_nr_next == -1)
11859     {
11860       setScreenCenteredToAllPlayers(&sx, &sy);
11861     }
11862     else
11863     {
11864       sx = stored_player[game.centered_player_nr_next].jx;
11865       sy = stored_player[game.centered_player_nr_next].jy;
11866     }
11867
11868     game.centered_player_nr = game.centered_player_nr_next;
11869     game.set_centered_player = FALSE;
11870
11871     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11872     DrawGameDoorValues();
11873   }
11874
11875   for (i = 0; i < MAX_PLAYERS; i++)
11876   {
11877     int actual_player_action = stored_player[i].effective_action;
11878
11879 #if 1
11880     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11881        - rnd_equinox_tetrachloride 048
11882        - rnd_equinox_tetrachloride_ii 096
11883        - rnd_emanuel_schmieg 002
11884        - doctor_sloan_ww 001, 020
11885     */
11886     if (stored_player[i].MovPos == 0)
11887       CheckGravityMovement(&stored_player[i]);
11888 #endif
11889
11890     // overwrite programmed action with tape action
11891     if (stored_player[i].programmed_action)
11892       actual_player_action = stored_player[i].programmed_action;
11893
11894     PlayerActions(&stored_player[i], actual_player_action);
11895
11896     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11897   }
11898
11899   ScrollScreen(NULL, SCROLL_GO_ON);
11900
11901   /* for backwards compatibility, the following code emulates a fixed bug that
11902      occured when pushing elements (causing elements that just made their last
11903      pushing step to already (if possible) make their first falling step in the
11904      same game frame, which is bad); this code is also needed to use the famous
11905      "spring push bug" which is used in older levels and might be wanted to be
11906      used also in newer levels, but in this case the buggy pushing code is only
11907      affecting the "spring" element and no other elements */
11908
11909   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11910   {
11911     for (i = 0; i < MAX_PLAYERS; i++)
11912     {
11913       struct PlayerInfo *player = &stored_player[i];
11914       int x = player->jx;
11915       int y = player->jy;
11916
11917       if (player->active && player->is_pushing && player->is_moving &&
11918           IS_MOVING(x, y) &&
11919           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11920            Tile[x][y] == EL_SPRING))
11921       {
11922         ContinueMoving(x, y);
11923
11924         // continue moving after pushing (this is actually a bug)
11925         if (!IS_MOVING(x, y))
11926           Stop[x][y] = FALSE;
11927       }
11928     }
11929   }
11930
11931   SCAN_PLAYFIELD(x, y)
11932   {
11933     Last[x][y] = Tile[x][y];
11934
11935     ChangeCount[x][y] = 0;
11936     ChangeEvent[x][y] = -1;
11937
11938     // this must be handled before main playfield loop
11939     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
11940     {
11941       MovDelay[x][y]--;
11942       if (MovDelay[x][y] <= 0)
11943         RemoveField(x, y);
11944     }
11945
11946     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
11947     {
11948       MovDelay[x][y]--;
11949       if (MovDelay[x][y] <= 0)
11950       {
11951         RemoveField(x, y);
11952         TEST_DrawLevelField(x, y);
11953
11954         TestIfElementTouchesCustomElement(x, y);        // for empty space
11955       }
11956     }
11957
11958 #if DEBUG
11959     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11960     {
11961       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
11962             x, y);
11963       Debug("game:playing:GameActions_RND", "This should never happen!");
11964
11965       ChangePage[x][y] = -1;
11966     }
11967 #endif
11968
11969     Stop[x][y] = FALSE;
11970     if (WasJustMoving[x][y] > 0)
11971       WasJustMoving[x][y]--;
11972     if (WasJustFalling[x][y] > 0)
11973       WasJustFalling[x][y]--;
11974     if (CheckCollision[x][y] > 0)
11975       CheckCollision[x][y]--;
11976     if (CheckImpact[x][y] > 0)
11977       CheckImpact[x][y]--;
11978
11979     GfxFrame[x][y]++;
11980
11981     /* reset finished pushing action (not done in ContinueMoving() to allow
11982        continuous pushing animation for elements with zero push delay) */
11983     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11984     {
11985       ResetGfxAnimation(x, y);
11986       TEST_DrawLevelField(x, y);
11987     }
11988
11989 #if DEBUG
11990     if (IS_BLOCKED(x, y))
11991     {
11992       int oldx, oldy;
11993
11994       Blocked2Moving(x, y, &oldx, &oldy);
11995       if (!IS_MOVING(oldx, oldy))
11996       {
11997         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
11998         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
11999         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12000         Debug("game:playing:GameActions_RND", "This should never happen!");
12001       }
12002     }
12003 #endif
12004   }
12005
12006   if (mouse_action.button)
12007   {
12008     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12009
12010     x = mouse_action.lx;
12011     y = mouse_action.ly;
12012     element = Tile[x][y];
12013
12014     if (new_button)
12015     {
12016       CheckElementChange(x, y, element, EL_UNDEFINED, CE_CLICKED_BY_MOUSE);
12017       CheckTriggeredElementChange(x, y, element, CE_MOUSE_CLICKED_ON_X);
12018     }
12019
12020     CheckElementChange(x, y, element, EL_UNDEFINED, CE_PRESSED_BY_MOUSE);
12021     CheckTriggeredElementChange(x, y, element, CE_MOUSE_PRESSED_ON_X);
12022   }
12023
12024   SCAN_PLAYFIELD(x, y)
12025   {
12026     element = Tile[x][y];
12027     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12028     last_gfx_frame = GfxFrame[x][y];
12029
12030     ResetGfxFrame(x, y);
12031
12032     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12033       DrawLevelGraphicAnimation(x, y, graphic);
12034
12035     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12036         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12037       ResetRandomAnimationValue(x, y);
12038
12039     SetRandomAnimationValue(x, y);
12040
12041     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12042
12043     if (IS_INACTIVE(element))
12044     {
12045       if (IS_ANIMATED(graphic))
12046         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12047
12048       continue;
12049     }
12050
12051     // this may take place after moving, so 'element' may have changed
12052     if (IS_CHANGING(x, y) &&
12053         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12054     {
12055       int page = element_info[element].event_page_nr[CE_DELAY];
12056
12057       HandleElementChange(x, y, page);
12058
12059       element = Tile[x][y];
12060       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12061     }
12062
12063     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12064     {
12065       StartMoving(x, y);
12066
12067       element = Tile[x][y];
12068       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12069
12070       if (IS_ANIMATED(graphic) &&
12071           !IS_MOVING(x, y) &&
12072           !Stop[x][y])
12073         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12074
12075       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12076         TEST_DrawTwinkleOnField(x, y);
12077     }
12078     else if (element == EL_ACID)
12079     {
12080       if (!Stop[x][y])
12081         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12082     }
12083     else if ((element == EL_EXIT_OPEN ||
12084               element == EL_EM_EXIT_OPEN ||
12085               element == EL_SP_EXIT_OPEN ||
12086               element == EL_STEEL_EXIT_OPEN ||
12087               element == EL_EM_STEEL_EXIT_OPEN ||
12088               element == EL_SP_TERMINAL ||
12089               element == EL_SP_TERMINAL_ACTIVE ||
12090               element == EL_EXTRA_TIME ||
12091               element == EL_SHIELD_NORMAL ||
12092               element == EL_SHIELD_DEADLY) &&
12093              IS_ANIMATED(graphic))
12094       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12095     else if (IS_MOVING(x, y))
12096       ContinueMoving(x, y);
12097     else if (IS_ACTIVE_BOMB(element))
12098       CheckDynamite(x, y);
12099     else if (element == EL_AMOEBA_GROWING)
12100       AmoebaGrowing(x, y);
12101     else if (element == EL_AMOEBA_SHRINKING)
12102       AmoebaShrinking(x, y);
12103
12104 #if !USE_NEW_AMOEBA_CODE
12105     else if (IS_AMOEBALIVE(element))
12106       AmoebaReproduce(x, y);
12107 #endif
12108
12109     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12110       Life(x, y);
12111     else if (element == EL_EXIT_CLOSED)
12112       CheckExit(x, y);
12113     else if (element == EL_EM_EXIT_CLOSED)
12114       CheckExitEM(x, y);
12115     else if (element == EL_STEEL_EXIT_CLOSED)
12116       CheckExitSteel(x, y);
12117     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12118       CheckExitSteelEM(x, y);
12119     else if (element == EL_SP_EXIT_CLOSED)
12120       CheckExitSP(x, y);
12121     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12122              element == EL_EXPANDABLE_STEELWALL_GROWING)
12123       MauerWaechst(x, y);
12124     else if (element == EL_EXPANDABLE_WALL ||
12125              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12126              element == EL_EXPANDABLE_WALL_VERTICAL ||
12127              element == EL_EXPANDABLE_WALL_ANY ||
12128              element == EL_BD_EXPANDABLE_WALL)
12129       MauerAbleger(x, y);
12130     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12131              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12132              element == EL_EXPANDABLE_STEELWALL_ANY)
12133       MauerAblegerStahl(x, y);
12134     else if (element == EL_FLAMES)
12135       CheckForDragon(x, y);
12136     else if (element == EL_EXPLOSION)
12137       ; // drawing of correct explosion animation is handled separately
12138     else if (element == EL_ELEMENT_SNAPPING ||
12139              element == EL_DIAGONAL_SHRINKING ||
12140              element == EL_DIAGONAL_GROWING)
12141     {
12142       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12143
12144       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12145     }
12146     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12147       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12148
12149     if (IS_BELT_ACTIVE(element))
12150       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12151
12152     if (game.magic_wall_active)
12153     {
12154       int jx = local_player->jx, jy = local_player->jy;
12155
12156       // play the element sound at the position nearest to the player
12157       if ((element == EL_MAGIC_WALL_FULL ||
12158            element == EL_MAGIC_WALL_ACTIVE ||
12159            element == EL_MAGIC_WALL_EMPTYING ||
12160            element == EL_BD_MAGIC_WALL_FULL ||
12161            element == EL_BD_MAGIC_WALL_ACTIVE ||
12162            element == EL_BD_MAGIC_WALL_EMPTYING ||
12163            element == EL_DC_MAGIC_WALL_FULL ||
12164            element == EL_DC_MAGIC_WALL_ACTIVE ||
12165            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12166           ABS(x - jx) + ABS(y - jy) <
12167           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12168       {
12169         magic_wall_x = x;
12170         magic_wall_y = y;
12171       }
12172     }
12173   }
12174
12175 #if USE_NEW_AMOEBA_CODE
12176   // new experimental amoeba growth stuff
12177   if (!(FrameCounter % 8))
12178   {
12179     static unsigned int random = 1684108901;
12180
12181     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12182     {
12183       x = RND(lev_fieldx);
12184       y = RND(lev_fieldy);
12185       element = Tile[x][y];
12186
12187       if (!IS_PLAYER(x,y) &&
12188           (element == EL_EMPTY ||
12189            CAN_GROW_INTO(element) ||
12190            element == EL_QUICKSAND_EMPTY ||
12191            element == EL_QUICKSAND_FAST_EMPTY ||
12192            element == EL_ACID_SPLASH_LEFT ||
12193            element == EL_ACID_SPLASH_RIGHT))
12194       {
12195         if ((IN_LEV_FIELD(x, y-1) && Tile[x][y-1] == EL_AMOEBA_WET) ||
12196             (IN_LEV_FIELD(x-1, y) && Tile[x-1][y] == EL_AMOEBA_WET) ||
12197             (IN_LEV_FIELD(x+1, y) && Tile[x+1][y] == EL_AMOEBA_WET) ||
12198             (IN_LEV_FIELD(x, y+1) && Tile[x][y+1] == EL_AMOEBA_WET))
12199           Tile[x][y] = EL_AMOEBA_DROP;
12200       }
12201
12202       random = random * 129 + 1;
12203     }
12204   }
12205 #endif
12206
12207   game.explosions_delayed = FALSE;
12208
12209   SCAN_PLAYFIELD(x, y)
12210   {
12211     element = Tile[x][y];
12212
12213     if (ExplodeField[x][y])
12214       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12215     else if (element == EL_EXPLOSION)
12216       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12217
12218     ExplodeField[x][y] = EX_TYPE_NONE;
12219   }
12220
12221   game.explosions_delayed = TRUE;
12222
12223   if (game.magic_wall_active)
12224   {
12225     if (!(game.magic_wall_time_left % 4))
12226     {
12227       int element = Tile[magic_wall_x][magic_wall_y];
12228
12229       if (element == EL_BD_MAGIC_WALL_FULL ||
12230           element == EL_BD_MAGIC_WALL_ACTIVE ||
12231           element == EL_BD_MAGIC_WALL_EMPTYING)
12232         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12233       else if (element == EL_DC_MAGIC_WALL_FULL ||
12234                element == EL_DC_MAGIC_WALL_ACTIVE ||
12235                element == EL_DC_MAGIC_WALL_EMPTYING)
12236         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12237       else
12238         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12239     }
12240
12241     if (game.magic_wall_time_left > 0)
12242     {
12243       game.magic_wall_time_left--;
12244
12245       if (!game.magic_wall_time_left)
12246       {
12247         SCAN_PLAYFIELD(x, y)
12248         {
12249           element = Tile[x][y];
12250
12251           if (element == EL_MAGIC_WALL_ACTIVE ||
12252               element == EL_MAGIC_WALL_FULL)
12253           {
12254             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12255             TEST_DrawLevelField(x, y);
12256           }
12257           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12258                    element == EL_BD_MAGIC_WALL_FULL)
12259           {
12260             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12261             TEST_DrawLevelField(x, y);
12262           }
12263           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12264                    element == EL_DC_MAGIC_WALL_FULL)
12265           {
12266             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12267             TEST_DrawLevelField(x, y);
12268           }
12269         }
12270
12271         game.magic_wall_active = FALSE;
12272       }
12273     }
12274   }
12275
12276   if (game.light_time_left > 0)
12277   {
12278     game.light_time_left--;
12279
12280     if (game.light_time_left == 0)
12281       RedrawAllLightSwitchesAndInvisibleElements();
12282   }
12283
12284   if (game.timegate_time_left > 0)
12285   {
12286     game.timegate_time_left--;
12287
12288     if (game.timegate_time_left == 0)
12289       CloseAllOpenTimegates();
12290   }
12291
12292   if (game.lenses_time_left > 0)
12293   {
12294     game.lenses_time_left--;
12295
12296     if (game.lenses_time_left == 0)
12297       RedrawAllInvisibleElementsForLenses();
12298   }
12299
12300   if (game.magnify_time_left > 0)
12301   {
12302     game.magnify_time_left--;
12303
12304     if (game.magnify_time_left == 0)
12305       RedrawAllInvisibleElementsForMagnifier();
12306   }
12307
12308   for (i = 0; i < MAX_PLAYERS; i++)
12309   {
12310     struct PlayerInfo *player = &stored_player[i];
12311
12312     if (SHIELD_ON(player))
12313     {
12314       if (player->shield_deadly_time_left)
12315         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12316       else if (player->shield_normal_time_left)
12317         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12318     }
12319   }
12320
12321 #if USE_DELAYED_GFX_REDRAW
12322   SCAN_PLAYFIELD(x, y)
12323   {
12324     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12325     {
12326       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12327          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12328
12329       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12330         DrawLevelField(x, y);
12331
12332       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12333         DrawLevelFieldCrumbled(x, y);
12334
12335       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12336         DrawLevelFieldCrumbledNeighbours(x, y);
12337
12338       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12339         DrawTwinkleOnField(x, y);
12340     }
12341
12342     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12343   }
12344 #endif
12345
12346   DrawAllPlayers();
12347   PlayAllPlayersSound();
12348
12349   for (i = 0; i < MAX_PLAYERS; i++)
12350   {
12351     struct PlayerInfo *player = &stored_player[i];
12352
12353     if (player->show_envelope != 0 && (!player->active ||
12354                                        player->MovPos == 0))
12355     {
12356       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12357
12358       player->show_envelope = 0;
12359     }
12360   }
12361
12362   // use random number generator in every frame to make it less predictable
12363   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12364     RND(1);
12365
12366   mouse_action_last = mouse_action;
12367 }
12368
12369 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12370 {
12371   int min_x = x, min_y = y, max_x = x, max_y = y;
12372   int i;
12373
12374   for (i = 0; i < MAX_PLAYERS; i++)
12375   {
12376     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12377
12378     if (!stored_player[i].active || &stored_player[i] == player)
12379       continue;
12380
12381     min_x = MIN(min_x, jx);
12382     min_y = MIN(min_y, jy);
12383     max_x = MAX(max_x, jx);
12384     max_y = MAX(max_y, jy);
12385   }
12386
12387   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12388 }
12389
12390 static boolean AllPlayersInVisibleScreen(void)
12391 {
12392   int i;
12393
12394   for (i = 0; i < MAX_PLAYERS; i++)
12395   {
12396     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12397
12398     if (!stored_player[i].active)
12399       continue;
12400
12401     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12402       return FALSE;
12403   }
12404
12405   return TRUE;
12406 }
12407
12408 void ScrollLevel(int dx, int dy)
12409 {
12410   int scroll_offset = 2 * TILEX_VAR;
12411   int x, y;
12412
12413   BlitBitmap(drawto_field, drawto_field,
12414              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12415              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12416              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12417              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12418              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12419              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12420
12421   if (dx != 0)
12422   {
12423     x = (dx == 1 ? BX1 : BX2);
12424     for (y = BY1; y <= BY2; y++)
12425       DrawScreenField(x, y);
12426   }
12427
12428   if (dy != 0)
12429   {
12430     y = (dy == 1 ? BY1 : BY2);
12431     for (x = BX1; x <= BX2; x++)
12432       DrawScreenField(x, y);
12433   }
12434
12435   redraw_mask |= REDRAW_FIELD;
12436 }
12437
12438 static boolean canFallDown(struct PlayerInfo *player)
12439 {
12440   int jx = player->jx, jy = player->jy;
12441
12442   return (IN_LEV_FIELD(jx, jy + 1) &&
12443           (IS_FREE(jx, jy + 1) ||
12444            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12445           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12446           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12447 }
12448
12449 static boolean canPassField(int x, int y, int move_dir)
12450 {
12451   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12452   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12453   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12454   int nextx = x + dx;
12455   int nexty = y + dy;
12456   int element = Tile[x][y];
12457
12458   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12459           !CAN_MOVE(element) &&
12460           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12461           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12462           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12463 }
12464
12465 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12466 {
12467   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12468   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12469   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12470   int newx = x + dx;
12471   int newy = y + dy;
12472
12473   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12474           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12475           (IS_DIGGABLE(Tile[newx][newy]) ||
12476            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12477            canPassField(newx, newy, move_dir)));
12478 }
12479
12480 static void CheckGravityMovement(struct PlayerInfo *player)
12481 {
12482   if (player->gravity && !player->programmed_action)
12483   {
12484     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12485     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12486     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12487     int jx = player->jx, jy = player->jy;
12488     boolean player_is_moving_to_valid_field =
12489       (!player_is_snapping &&
12490        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12491         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12492     boolean player_can_fall_down = canFallDown(player);
12493
12494     if (player_can_fall_down &&
12495         !player_is_moving_to_valid_field)
12496       player->programmed_action = MV_DOWN;
12497   }
12498 }
12499
12500 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12501 {
12502   return CheckGravityMovement(player);
12503
12504   if (player->gravity && !player->programmed_action)
12505   {
12506     int jx = player->jx, jy = player->jy;
12507     boolean field_under_player_is_free =
12508       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12509     boolean player_is_standing_on_valid_field =
12510       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12511        (IS_WALKABLE(Tile[jx][jy]) &&
12512         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12513
12514     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12515       player->programmed_action = MV_DOWN;
12516   }
12517 }
12518
12519 /*
12520   MovePlayerOneStep()
12521   -----------------------------------------------------------------------------
12522   dx, dy:               direction (non-diagonal) to try to move the player to
12523   real_dx, real_dy:     direction as read from input device (can be diagonal)
12524 */
12525
12526 boolean MovePlayerOneStep(struct PlayerInfo *player,
12527                           int dx, int dy, int real_dx, int real_dy)
12528 {
12529   int jx = player->jx, jy = player->jy;
12530   int new_jx = jx + dx, new_jy = jy + dy;
12531   int can_move;
12532   boolean player_can_move = !player->cannot_move;
12533
12534   if (!player->active || (!dx && !dy))
12535     return MP_NO_ACTION;
12536
12537   player->MovDir = (dx < 0 ? MV_LEFT :
12538                     dx > 0 ? MV_RIGHT :
12539                     dy < 0 ? MV_UP :
12540                     dy > 0 ? MV_DOWN :  MV_NONE);
12541
12542   if (!IN_LEV_FIELD(new_jx, new_jy))
12543     return MP_NO_ACTION;
12544
12545   if (!player_can_move)
12546   {
12547     if (player->MovPos == 0)
12548     {
12549       player->is_moving = FALSE;
12550       player->is_digging = FALSE;
12551       player->is_collecting = FALSE;
12552       player->is_snapping = FALSE;
12553       player->is_pushing = FALSE;
12554     }
12555   }
12556
12557   if (!network.enabled && game.centered_player_nr == -1 &&
12558       !AllPlayersInSight(player, new_jx, new_jy))
12559     return MP_NO_ACTION;
12560
12561   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12562   if (can_move != MP_MOVING)
12563     return can_move;
12564
12565   // check if DigField() has caused relocation of the player
12566   if (player->jx != jx || player->jy != jy)
12567     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12568
12569   StorePlayer[jx][jy] = 0;
12570   player->last_jx = jx;
12571   player->last_jy = jy;
12572   player->jx = new_jx;
12573   player->jy = new_jy;
12574   StorePlayer[new_jx][new_jy] = player->element_nr;
12575
12576   if (player->move_delay_value_next != -1)
12577   {
12578     player->move_delay_value = player->move_delay_value_next;
12579     player->move_delay_value_next = -1;
12580   }
12581
12582   player->MovPos =
12583     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12584
12585   player->step_counter++;
12586
12587   PlayerVisit[jx][jy] = FrameCounter;
12588
12589   player->is_moving = TRUE;
12590
12591 #if 1
12592   // should better be called in MovePlayer(), but this breaks some tapes
12593   ScrollPlayer(player, SCROLL_INIT);
12594 #endif
12595
12596   return MP_MOVING;
12597 }
12598
12599 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12600 {
12601   int jx = player->jx, jy = player->jy;
12602   int old_jx = jx, old_jy = jy;
12603   int moved = MP_NO_ACTION;
12604
12605   if (!player->active)
12606     return FALSE;
12607
12608   if (!dx && !dy)
12609   {
12610     if (player->MovPos == 0)
12611     {
12612       player->is_moving = FALSE;
12613       player->is_digging = FALSE;
12614       player->is_collecting = FALSE;
12615       player->is_snapping = FALSE;
12616       player->is_pushing = FALSE;
12617     }
12618
12619     return FALSE;
12620   }
12621
12622   if (player->move_delay > 0)
12623     return FALSE;
12624
12625   player->move_delay = -1;              // set to "uninitialized" value
12626
12627   // store if player is automatically moved to next field
12628   player->is_auto_moving = (player->programmed_action != MV_NONE);
12629
12630   // remove the last programmed player action
12631   player->programmed_action = 0;
12632
12633   if (player->MovPos)
12634   {
12635     // should only happen if pre-1.2 tape recordings are played
12636     // this is only for backward compatibility
12637
12638     int original_move_delay_value = player->move_delay_value;
12639
12640 #if DEBUG
12641     Debug("game:playing:MovePlayer",
12642           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12643           tape.counter);
12644 #endif
12645
12646     // scroll remaining steps with finest movement resolution
12647     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12648
12649     while (player->MovPos)
12650     {
12651       ScrollPlayer(player, SCROLL_GO_ON);
12652       ScrollScreen(NULL, SCROLL_GO_ON);
12653
12654       AdvanceFrameAndPlayerCounters(player->index_nr);
12655
12656       DrawAllPlayers();
12657       BackToFront_WithFrameDelay(0);
12658     }
12659
12660     player->move_delay_value = original_move_delay_value;
12661   }
12662
12663   player->is_active = FALSE;
12664
12665   if (player->last_move_dir & MV_HORIZONTAL)
12666   {
12667     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12668       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12669   }
12670   else
12671   {
12672     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12673       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12674   }
12675
12676   if (!moved && !player->is_active)
12677   {
12678     player->is_moving = FALSE;
12679     player->is_digging = FALSE;
12680     player->is_collecting = FALSE;
12681     player->is_snapping = FALSE;
12682     player->is_pushing = FALSE;
12683   }
12684
12685   jx = player->jx;
12686   jy = player->jy;
12687
12688   if (moved & MP_MOVING && !ScreenMovPos &&
12689       (player->index_nr == game.centered_player_nr ||
12690        game.centered_player_nr == -1))
12691   {
12692     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12693
12694     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12695     {
12696       // actual player has left the screen -- scroll in that direction
12697       if (jx != old_jx)         // player has moved horizontally
12698         scroll_x += (jx - old_jx);
12699       else                      // player has moved vertically
12700         scroll_y += (jy - old_jy);
12701     }
12702     else
12703     {
12704       int offset_raw = game.scroll_delay_value;
12705
12706       if (jx != old_jx)         // player has moved horizontally
12707       {
12708         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12709         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12710         int new_scroll_x = jx - MIDPOSX + offset_x;
12711
12712         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12713             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12714           scroll_x = new_scroll_x;
12715
12716         // don't scroll over playfield boundaries
12717         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12718
12719         // don't scroll more than one field at a time
12720         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12721
12722         // don't scroll against the player's moving direction
12723         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12724             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12725           scroll_x = old_scroll_x;
12726       }
12727       else                      // player has moved vertically
12728       {
12729         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12730         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12731         int new_scroll_y = jy - MIDPOSY + offset_y;
12732
12733         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12734             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12735           scroll_y = new_scroll_y;
12736
12737         // don't scroll over playfield boundaries
12738         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
12739
12740         // don't scroll more than one field at a time
12741         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12742
12743         // don't scroll against the player's moving direction
12744         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12745             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12746           scroll_y = old_scroll_y;
12747       }
12748     }
12749
12750     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12751     {
12752       if (!network.enabled && game.centered_player_nr == -1 &&
12753           !AllPlayersInVisibleScreen())
12754       {
12755         scroll_x = old_scroll_x;
12756         scroll_y = old_scroll_y;
12757       }
12758       else
12759       {
12760         ScrollScreen(player, SCROLL_INIT);
12761         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12762       }
12763     }
12764   }
12765
12766   player->StepFrame = 0;
12767
12768   if (moved & MP_MOVING)
12769   {
12770     if (old_jx != jx && old_jy == jy)
12771       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12772     else if (old_jx == jx && old_jy != jy)
12773       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12774
12775     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12776
12777     player->last_move_dir = player->MovDir;
12778     player->is_moving = TRUE;
12779     player->is_snapping = FALSE;
12780     player->is_switching = FALSE;
12781     player->is_dropping = FALSE;
12782     player->is_dropping_pressed = FALSE;
12783     player->drop_pressed_delay = 0;
12784
12785 #if 0
12786     // should better be called here than above, but this breaks some tapes
12787     ScrollPlayer(player, SCROLL_INIT);
12788 #endif
12789   }
12790   else
12791   {
12792     CheckGravityMovementWhenNotMoving(player);
12793
12794     player->is_moving = FALSE;
12795
12796     /* at this point, the player is allowed to move, but cannot move right now
12797        (e.g. because of something blocking the way) -- ensure that the player
12798        is also allowed to move in the next frame (in old versions before 3.1.1,
12799        the player was forced to wait again for eight frames before next try) */
12800
12801     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12802       player->move_delay = 0;   // allow direct movement in the next frame
12803   }
12804
12805   if (player->move_delay == -1)         // not yet initialized by DigField()
12806     player->move_delay = player->move_delay_value;
12807
12808   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12809   {
12810     TestIfPlayerTouchesBadThing(jx, jy);
12811     TestIfPlayerTouchesCustomElement(jx, jy);
12812   }
12813
12814   if (!player->active)
12815     RemovePlayer(player);
12816
12817   return moved;
12818 }
12819
12820 void ScrollPlayer(struct PlayerInfo *player, int mode)
12821 {
12822   int jx = player->jx, jy = player->jy;
12823   int last_jx = player->last_jx, last_jy = player->last_jy;
12824   int move_stepsize = TILEX / player->move_delay_value;
12825
12826   if (!player->active)
12827     return;
12828
12829   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
12830     return;
12831
12832   if (mode == SCROLL_INIT)
12833   {
12834     player->actual_frame_counter = FrameCounter;
12835     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12836
12837     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12838         Tile[last_jx][last_jy] == EL_EMPTY)
12839     {
12840       int last_field_block_delay = 0;   // start with no blocking at all
12841       int block_delay_adjustment = player->block_delay_adjustment;
12842
12843       // if player blocks last field, add delay for exactly one move
12844       if (player->block_last_field)
12845       {
12846         last_field_block_delay += player->move_delay_value;
12847
12848         // when blocking enabled, prevent moving up despite gravity
12849         if (player->gravity && player->MovDir == MV_UP)
12850           block_delay_adjustment = -1;
12851       }
12852
12853       // add block delay adjustment (also possible when not blocking)
12854       last_field_block_delay += block_delay_adjustment;
12855
12856       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12857       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12858     }
12859
12860     if (player->MovPos != 0)    // player has not yet reached destination
12861       return;
12862   }
12863   else if (!FrameReached(&player->actual_frame_counter, 1))
12864     return;
12865
12866   if (player->MovPos != 0)
12867   {
12868     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12869     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12870
12871     // before DrawPlayer() to draw correct player graphic for this case
12872     if (player->MovPos == 0)
12873       CheckGravityMovement(player);
12874   }
12875
12876   if (player->MovPos == 0)      // player reached destination field
12877   {
12878     if (player->move_delay_reset_counter > 0)
12879     {
12880       player->move_delay_reset_counter--;
12881
12882       if (player->move_delay_reset_counter == 0)
12883       {
12884         // continue with normal speed after quickly moving through gate
12885         HALVE_PLAYER_SPEED(player);
12886
12887         // be able to make the next move without delay
12888         player->move_delay = 0;
12889       }
12890     }
12891
12892     player->last_jx = jx;
12893     player->last_jy = jy;
12894
12895     if (Tile[jx][jy] == EL_EXIT_OPEN ||
12896         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
12897         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
12898         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
12899         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12900         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12901         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
12902         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
12903     {
12904       ExitPlayer(player);
12905
12906       if (game.players_still_needed == 0 &&
12907           (game.friends_still_needed == 0 ||
12908            IS_SP_ELEMENT(Tile[jx][jy])))
12909         LevelSolved();
12910     }
12911
12912     // this breaks one level: "machine", level 000
12913     {
12914       int move_direction = player->MovDir;
12915       int enter_side = MV_DIR_OPPOSITE(move_direction);
12916       int leave_side = move_direction;
12917       int old_jx = last_jx;
12918       int old_jy = last_jy;
12919       int old_element = Tile[old_jx][old_jy];
12920       int new_element = Tile[jx][jy];
12921
12922       if (IS_CUSTOM_ELEMENT(old_element))
12923         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12924                                    CE_LEFT_BY_PLAYER,
12925                                    player->index_bit, leave_side);
12926
12927       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12928                                           CE_PLAYER_LEAVES_X,
12929                                           player->index_bit, leave_side);
12930
12931       if (IS_CUSTOM_ELEMENT(new_element))
12932         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12933                                    player->index_bit, enter_side);
12934
12935       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12936                                           CE_PLAYER_ENTERS_X,
12937                                           player->index_bit, enter_side);
12938
12939       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12940                                         CE_MOVE_OF_X, move_direction);
12941     }
12942
12943     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12944     {
12945       TestIfPlayerTouchesBadThing(jx, jy);
12946       TestIfPlayerTouchesCustomElement(jx, jy);
12947
12948       /* needed because pushed element has not yet reached its destination,
12949          so it would trigger a change event at its previous field location */
12950       if (!player->is_pushing)
12951         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
12952
12953       if (!player->active)
12954         RemovePlayer(player);
12955     }
12956
12957     if (!game.LevelSolved && level.use_step_counter)
12958     {
12959       int i;
12960
12961       TimePlayed++;
12962
12963       if (TimeLeft > 0)
12964       {
12965         TimeLeft--;
12966
12967         if (TimeLeft <= 10 && setup.time_limit)
12968           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12969
12970         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12971
12972         DisplayGameControlValues();
12973
12974         if (!TimeLeft && setup.time_limit)
12975           for (i = 0; i < MAX_PLAYERS; i++)
12976             KillPlayer(&stored_player[i]);
12977       }
12978       else if (game.no_time_limit && !game.all_players_gone)
12979       {
12980         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12981
12982         DisplayGameControlValues();
12983       }
12984     }
12985
12986     if (tape.single_step && tape.recording && !tape.pausing &&
12987         !player->programmed_action)
12988       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12989
12990     if (!player->programmed_action)
12991       CheckSaveEngineSnapshot(player);
12992   }
12993 }
12994
12995 void ScrollScreen(struct PlayerInfo *player, int mode)
12996 {
12997   static unsigned int screen_frame_counter = 0;
12998
12999   if (mode == SCROLL_INIT)
13000   {
13001     // set scrolling step size according to actual player's moving speed
13002     ScrollStepSize = TILEX / player->move_delay_value;
13003
13004     screen_frame_counter = FrameCounter;
13005     ScreenMovDir = player->MovDir;
13006     ScreenMovPos = player->MovPos;
13007     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13008     return;
13009   }
13010   else if (!FrameReached(&screen_frame_counter, 1))
13011     return;
13012
13013   if (ScreenMovPos)
13014   {
13015     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13016     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13017     redraw_mask |= REDRAW_FIELD;
13018   }
13019   else
13020     ScreenMovDir = MV_NONE;
13021 }
13022
13023 void TestIfPlayerTouchesCustomElement(int x, int y)
13024 {
13025   static int xy[4][2] =
13026   {
13027     { 0, -1 },
13028     { -1, 0 },
13029     { +1, 0 },
13030     { 0, +1 }
13031   };
13032   static int trigger_sides[4][2] =
13033   {
13034     // center side       border side
13035     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13036     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13037     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13038     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13039   };
13040   static int touch_dir[4] =
13041   {
13042     MV_LEFT | MV_RIGHT,
13043     MV_UP   | MV_DOWN,
13044     MV_UP   | MV_DOWN,
13045     MV_LEFT | MV_RIGHT
13046   };
13047   int center_element = Tile[x][y];      // should always be non-moving!
13048   int i;
13049
13050   for (i = 0; i < NUM_DIRECTIONS; i++)
13051   {
13052     int xx = x + xy[i][0];
13053     int yy = y + xy[i][1];
13054     int center_side = trigger_sides[i][0];
13055     int border_side = trigger_sides[i][1];
13056     int border_element;
13057
13058     if (!IN_LEV_FIELD(xx, yy))
13059       continue;
13060
13061     if (IS_PLAYER(x, y))                // player found at center element
13062     {
13063       struct PlayerInfo *player = PLAYERINFO(x, y);
13064
13065       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13066         border_element = Tile[xx][yy];          // may be moving!
13067       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13068         border_element = Tile[xx][yy];
13069       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13070         border_element = MovingOrBlocked2Element(xx, yy);
13071       else
13072         continue;               // center and border element do not touch
13073
13074       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13075                                  player->index_bit, border_side);
13076       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13077                                           CE_PLAYER_TOUCHES_X,
13078                                           player->index_bit, border_side);
13079
13080       {
13081         /* use player element that is initially defined in the level playfield,
13082            not the player element that corresponds to the runtime player number
13083            (example: a level that contains EL_PLAYER_3 as the only player would
13084            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13085         int player_element = PLAYERINFO(x, y)->initial_element;
13086
13087         CheckElementChangeBySide(xx, yy, border_element, player_element,
13088                                  CE_TOUCHING_X, border_side);
13089       }
13090     }
13091     else if (IS_PLAYER(xx, yy))         // player found at border element
13092     {
13093       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13094
13095       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13096       {
13097         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13098           continue;             // center and border element do not touch
13099       }
13100
13101       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13102                                  player->index_bit, center_side);
13103       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13104                                           CE_PLAYER_TOUCHES_X,
13105                                           player->index_bit, center_side);
13106
13107       {
13108         /* use player element that is initially defined in the level playfield,
13109            not the player element that corresponds to the runtime player number
13110            (example: a level that contains EL_PLAYER_3 as the only player would
13111            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13112         int player_element = PLAYERINFO(xx, yy)->initial_element;
13113
13114         CheckElementChangeBySide(x, y, center_element, player_element,
13115                                  CE_TOUCHING_X, center_side);
13116       }
13117
13118       break;
13119     }
13120   }
13121 }
13122
13123 void TestIfElementTouchesCustomElement(int x, int y)
13124 {
13125   static int xy[4][2] =
13126   {
13127     { 0, -1 },
13128     { -1, 0 },
13129     { +1, 0 },
13130     { 0, +1 }
13131   };
13132   static int trigger_sides[4][2] =
13133   {
13134     // center side      border side
13135     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13136     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13137     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13138     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13139   };
13140   static int touch_dir[4] =
13141   {
13142     MV_LEFT | MV_RIGHT,
13143     MV_UP   | MV_DOWN,
13144     MV_UP   | MV_DOWN,
13145     MV_LEFT | MV_RIGHT
13146   };
13147   boolean change_center_element = FALSE;
13148   int center_element = Tile[x][y];      // should always be non-moving!
13149   int border_element_old[NUM_DIRECTIONS];
13150   int i;
13151
13152   for (i = 0; i < NUM_DIRECTIONS; i++)
13153   {
13154     int xx = x + xy[i][0];
13155     int yy = y + xy[i][1];
13156     int border_element;
13157
13158     border_element_old[i] = -1;
13159
13160     if (!IN_LEV_FIELD(xx, yy))
13161       continue;
13162
13163     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13164       border_element = Tile[xx][yy];    // may be moving!
13165     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13166       border_element = Tile[xx][yy];
13167     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13168       border_element = MovingOrBlocked2Element(xx, yy);
13169     else
13170       continue;                 // center and border element do not touch
13171
13172     border_element_old[i] = border_element;
13173   }
13174
13175   for (i = 0; i < NUM_DIRECTIONS; i++)
13176   {
13177     int xx = x + xy[i][0];
13178     int yy = y + xy[i][1];
13179     int center_side = trigger_sides[i][0];
13180     int border_element = border_element_old[i];
13181
13182     if (border_element == -1)
13183       continue;
13184
13185     // check for change of border element
13186     CheckElementChangeBySide(xx, yy, border_element, center_element,
13187                              CE_TOUCHING_X, center_side);
13188
13189     // (center element cannot be player, so we dont have to check this here)
13190   }
13191
13192   for (i = 0; i < NUM_DIRECTIONS; i++)
13193   {
13194     int xx = x + xy[i][0];
13195     int yy = y + xy[i][1];
13196     int border_side = trigger_sides[i][1];
13197     int border_element = border_element_old[i];
13198
13199     if (border_element == -1)
13200       continue;
13201
13202     // check for change of center element (but change it only once)
13203     if (!change_center_element)
13204       change_center_element =
13205         CheckElementChangeBySide(x, y, center_element, border_element,
13206                                  CE_TOUCHING_X, border_side);
13207
13208     if (IS_PLAYER(xx, yy))
13209     {
13210       /* use player element that is initially defined in the level playfield,
13211          not the player element that corresponds to the runtime player number
13212          (example: a level that contains EL_PLAYER_3 as the only player would
13213          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13214       int player_element = PLAYERINFO(xx, yy)->initial_element;
13215
13216       CheckElementChangeBySide(x, y, center_element, player_element,
13217                                CE_TOUCHING_X, border_side);
13218     }
13219   }
13220 }
13221
13222 void TestIfElementHitsCustomElement(int x, int y, int direction)
13223 {
13224   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13225   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13226   int hitx = x + dx, hity = y + dy;
13227   int hitting_element = Tile[x][y];
13228   int touched_element;
13229
13230   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13231     return;
13232
13233   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13234                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13235
13236   if (IN_LEV_FIELD(hitx, hity))
13237   {
13238     int opposite_direction = MV_DIR_OPPOSITE(direction);
13239     int hitting_side = direction;
13240     int touched_side = opposite_direction;
13241     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13242                           MovDir[hitx][hity] != direction ||
13243                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13244
13245     object_hit = TRUE;
13246
13247     if (object_hit)
13248     {
13249       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13250                                CE_HITTING_X, touched_side);
13251
13252       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13253                                CE_HIT_BY_X, hitting_side);
13254
13255       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13256                                CE_HIT_BY_SOMETHING, opposite_direction);
13257
13258       if (IS_PLAYER(hitx, hity))
13259       {
13260         /* use player element that is initially defined in the level playfield,
13261            not the player element that corresponds to the runtime player number
13262            (example: a level that contains EL_PLAYER_3 as the only player would
13263            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13264         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13265
13266         CheckElementChangeBySide(x, y, hitting_element, player_element,
13267                                  CE_HITTING_X, touched_side);
13268       }
13269     }
13270   }
13271
13272   // "hitting something" is also true when hitting the playfield border
13273   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13274                            CE_HITTING_SOMETHING, direction);
13275 }
13276
13277 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13278 {
13279   int i, kill_x = -1, kill_y = -1;
13280
13281   int bad_element = -1;
13282   static int test_xy[4][2] =
13283   {
13284     { 0, -1 },
13285     { -1, 0 },
13286     { +1, 0 },
13287     { 0, +1 }
13288   };
13289   static int test_dir[4] =
13290   {
13291     MV_UP,
13292     MV_LEFT,
13293     MV_RIGHT,
13294     MV_DOWN
13295   };
13296
13297   for (i = 0; i < NUM_DIRECTIONS; i++)
13298   {
13299     int test_x, test_y, test_move_dir, test_element;
13300
13301     test_x = good_x + test_xy[i][0];
13302     test_y = good_y + test_xy[i][1];
13303
13304     if (!IN_LEV_FIELD(test_x, test_y))
13305       continue;
13306
13307     test_move_dir =
13308       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13309
13310     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13311
13312     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13313        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13314     */
13315     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13316         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13317     {
13318       kill_x = test_x;
13319       kill_y = test_y;
13320       bad_element = test_element;
13321
13322       break;
13323     }
13324   }
13325
13326   if (kill_x != -1 || kill_y != -1)
13327   {
13328     if (IS_PLAYER(good_x, good_y))
13329     {
13330       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13331
13332       if (player->shield_deadly_time_left > 0 &&
13333           !IS_INDESTRUCTIBLE(bad_element))
13334         Bang(kill_x, kill_y);
13335       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13336         KillPlayer(player);
13337     }
13338     else
13339       Bang(good_x, good_y);
13340   }
13341 }
13342
13343 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13344 {
13345   int i, kill_x = -1, kill_y = -1;
13346   int bad_element = Tile[bad_x][bad_y];
13347   static int test_xy[4][2] =
13348   {
13349     { 0, -1 },
13350     { -1, 0 },
13351     { +1, 0 },
13352     { 0, +1 }
13353   };
13354   static int touch_dir[4] =
13355   {
13356     MV_LEFT | MV_RIGHT,
13357     MV_UP   | MV_DOWN,
13358     MV_UP   | MV_DOWN,
13359     MV_LEFT | MV_RIGHT
13360   };
13361   static int test_dir[4] =
13362   {
13363     MV_UP,
13364     MV_LEFT,
13365     MV_RIGHT,
13366     MV_DOWN
13367   };
13368
13369   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13370     return;
13371
13372   for (i = 0; i < NUM_DIRECTIONS; i++)
13373   {
13374     int test_x, test_y, test_move_dir, test_element;
13375
13376     test_x = bad_x + test_xy[i][0];
13377     test_y = bad_y + test_xy[i][1];
13378
13379     if (!IN_LEV_FIELD(test_x, test_y))
13380       continue;
13381
13382     test_move_dir =
13383       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13384
13385     test_element = Tile[test_x][test_y];
13386
13387     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13388        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13389     */
13390     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13391         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13392     {
13393       // good thing is player or penguin that does not move away
13394       if (IS_PLAYER(test_x, test_y))
13395       {
13396         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13397
13398         if (bad_element == EL_ROBOT && player->is_moving)
13399           continue;     // robot does not kill player if he is moving
13400
13401         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13402         {
13403           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13404             continue;           // center and border element do not touch
13405         }
13406
13407         kill_x = test_x;
13408         kill_y = test_y;
13409
13410         break;
13411       }
13412       else if (test_element == EL_PENGUIN)
13413       {
13414         kill_x = test_x;
13415         kill_y = test_y;
13416
13417         break;
13418       }
13419     }
13420   }
13421
13422   if (kill_x != -1 || kill_y != -1)
13423   {
13424     if (IS_PLAYER(kill_x, kill_y))
13425     {
13426       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13427
13428       if (player->shield_deadly_time_left > 0 &&
13429           !IS_INDESTRUCTIBLE(bad_element))
13430         Bang(bad_x, bad_y);
13431       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13432         KillPlayer(player);
13433     }
13434     else
13435       Bang(kill_x, kill_y);
13436   }
13437 }
13438
13439 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13440 {
13441   int bad_element = Tile[bad_x][bad_y];
13442   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13443   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13444   int test_x = bad_x + dx, test_y = bad_y + dy;
13445   int test_move_dir, test_element;
13446   int kill_x = -1, kill_y = -1;
13447
13448   if (!IN_LEV_FIELD(test_x, test_y))
13449     return;
13450
13451   test_move_dir =
13452     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13453
13454   test_element = Tile[test_x][test_y];
13455
13456   if (test_move_dir != bad_move_dir)
13457   {
13458     // good thing can be player or penguin that does not move away
13459     if (IS_PLAYER(test_x, test_y))
13460     {
13461       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13462
13463       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13464          player as being hit when he is moving towards the bad thing, because
13465          the "get hit by" condition would be lost after the player stops) */
13466       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13467         return;         // player moves away from bad thing
13468
13469       kill_x = test_x;
13470       kill_y = test_y;
13471     }
13472     else if (test_element == EL_PENGUIN)
13473     {
13474       kill_x = test_x;
13475       kill_y = test_y;
13476     }
13477   }
13478
13479   if (kill_x != -1 || kill_y != -1)
13480   {
13481     if (IS_PLAYER(kill_x, kill_y))
13482     {
13483       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13484
13485       if (player->shield_deadly_time_left > 0 &&
13486           !IS_INDESTRUCTIBLE(bad_element))
13487         Bang(bad_x, bad_y);
13488       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13489         KillPlayer(player);
13490     }
13491     else
13492       Bang(kill_x, kill_y);
13493   }
13494 }
13495
13496 void TestIfPlayerTouchesBadThing(int x, int y)
13497 {
13498   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13499 }
13500
13501 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13502 {
13503   TestIfGoodThingHitsBadThing(x, y, move_dir);
13504 }
13505
13506 void TestIfBadThingTouchesPlayer(int x, int y)
13507 {
13508   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13509 }
13510
13511 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13512 {
13513   TestIfBadThingHitsGoodThing(x, y, move_dir);
13514 }
13515
13516 void TestIfFriendTouchesBadThing(int x, int y)
13517 {
13518   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13519 }
13520
13521 void TestIfBadThingTouchesFriend(int x, int y)
13522 {
13523   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13524 }
13525
13526 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13527 {
13528   int i, kill_x = bad_x, kill_y = bad_y;
13529   static int xy[4][2] =
13530   {
13531     { 0, -1 },
13532     { -1, 0 },
13533     { +1, 0 },
13534     { 0, +1 }
13535   };
13536
13537   for (i = 0; i < NUM_DIRECTIONS; i++)
13538   {
13539     int x, y, element;
13540
13541     x = bad_x + xy[i][0];
13542     y = bad_y + xy[i][1];
13543     if (!IN_LEV_FIELD(x, y))
13544       continue;
13545
13546     element = Tile[x][y];
13547     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13548         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13549     {
13550       kill_x = x;
13551       kill_y = y;
13552       break;
13553     }
13554   }
13555
13556   if (kill_x != bad_x || kill_y != bad_y)
13557     Bang(bad_x, bad_y);
13558 }
13559
13560 void KillPlayer(struct PlayerInfo *player)
13561 {
13562   int jx = player->jx, jy = player->jy;
13563
13564   if (!player->active)
13565     return;
13566
13567 #if 0
13568   Debug("game:playing:KillPlayer",
13569         "0: killed == %d, active == %d, reanimated == %d",
13570         player->killed, player->active, player->reanimated);
13571 #endif
13572
13573   /* the following code was introduced to prevent an infinite loop when calling
13574      -> Bang()
13575      -> CheckTriggeredElementChangeExt()
13576      -> ExecuteCustomElementAction()
13577      -> KillPlayer()
13578      -> (infinitely repeating the above sequence of function calls)
13579      which occurs when killing the player while having a CE with the setting
13580      "kill player X when explosion of <player X>"; the solution using a new
13581      field "player->killed" was chosen for backwards compatibility, although
13582      clever use of the fields "player->active" etc. would probably also work */
13583 #if 1
13584   if (player->killed)
13585     return;
13586 #endif
13587
13588   player->killed = TRUE;
13589
13590   // remove accessible field at the player's position
13591   Tile[jx][jy] = EL_EMPTY;
13592
13593   // deactivate shield (else Bang()/Explode() would not work right)
13594   player->shield_normal_time_left = 0;
13595   player->shield_deadly_time_left = 0;
13596
13597 #if 0
13598   Debug("game:playing:KillPlayer",
13599         "1: killed == %d, active == %d, reanimated == %d",
13600         player->killed, player->active, player->reanimated);
13601 #endif
13602
13603   Bang(jx, jy);
13604
13605 #if 0
13606   Debug("game:playing:KillPlayer",
13607         "2: killed == %d, active == %d, reanimated == %d",
13608         player->killed, player->active, player->reanimated);
13609 #endif
13610
13611   if (player->reanimated)       // killed player may have been reanimated
13612     player->killed = player->reanimated = FALSE;
13613   else
13614     BuryPlayer(player);
13615 }
13616
13617 static void KillPlayerUnlessEnemyProtected(int x, int y)
13618 {
13619   if (!PLAYER_ENEMY_PROTECTED(x, y))
13620     KillPlayer(PLAYERINFO(x, y));
13621 }
13622
13623 static void KillPlayerUnlessExplosionProtected(int x, int y)
13624 {
13625   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13626     KillPlayer(PLAYERINFO(x, y));
13627 }
13628
13629 void BuryPlayer(struct PlayerInfo *player)
13630 {
13631   int jx = player->jx, jy = player->jy;
13632
13633   if (!player->active)
13634     return;
13635
13636   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13637   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13638
13639   RemovePlayer(player);
13640
13641   player->buried = TRUE;
13642
13643   if (game.all_players_gone)
13644     game.GameOver = TRUE;
13645 }
13646
13647 void RemovePlayer(struct PlayerInfo *player)
13648 {
13649   int jx = player->jx, jy = player->jy;
13650   int i, found = FALSE;
13651
13652   player->present = FALSE;
13653   player->active = FALSE;
13654
13655   // required for some CE actions (even if the player is not active anymore)
13656   player->MovPos = 0;
13657
13658   if (!ExplodeField[jx][jy])
13659     StorePlayer[jx][jy] = 0;
13660
13661   if (player->is_moving)
13662     TEST_DrawLevelField(player->last_jx, player->last_jy);
13663
13664   for (i = 0; i < MAX_PLAYERS; i++)
13665     if (stored_player[i].active)
13666       found = TRUE;
13667
13668   if (!found)
13669   {
13670     game.all_players_gone = TRUE;
13671     game.GameOver = TRUE;
13672   }
13673
13674   game.exit_x = game.robot_wheel_x = jx;
13675   game.exit_y = game.robot_wheel_y = jy;
13676 }
13677
13678 void ExitPlayer(struct PlayerInfo *player)
13679 {
13680   DrawPlayer(player);   // needed here only to cleanup last field
13681   RemovePlayer(player);
13682
13683   if (game.players_still_needed > 0)
13684     game.players_still_needed--;
13685 }
13686
13687 static void setFieldForSnapping(int x, int y, int element, int direction)
13688 {
13689   struct ElementInfo *ei = &element_info[element];
13690   int direction_bit = MV_DIR_TO_BIT(direction);
13691   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13692   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13693                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13694
13695   Tile[x][y] = EL_ELEMENT_SNAPPING;
13696   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13697
13698   ResetGfxAnimation(x, y);
13699
13700   GfxElement[x][y] = element;
13701   GfxAction[x][y] = action;
13702   GfxDir[x][y] = direction;
13703   GfxFrame[x][y] = -1;
13704 }
13705
13706 /*
13707   =============================================================================
13708   checkDiagonalPushing()
13709   -----------------------------------------------------------------------------
13710   check if diagonal input device direction results in pushing of object
13711   (by checking if the alternative direction is walkable, diggable, ...)
13712   =============================================================================
13713 */
13714
13715 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13716                                     int x, int y, int real_dx, int real_dy)
13717 {
13718   int jx, jy, dx, dy, xx, yy;
13719
13720   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13721     return TRUE;
13722
13723   // diagonal direction: check alternative direction
13724   jx = player->jx;
13725   jy = player->jy;
13726   dx = x - jx;
13727   dy = y - jy;
13728   xx = jx + (dx == 0 ? real_dx : 0);
13729   yy = jy + (dy == 0 ? real_dy : 0);
13730
13731   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
13732 }
13733
13734 /*
13735   =============================================================================
13736   DigField()
13737   -----------------------------------------------------------------------------
13738   x, y:                 field next to player (non-diagonal) to try to dig to
13739   real_dx, real_dy:     direction as read from input device (can be diagonal)
13740   =============================================================================
13741 */
13742
13743 static int DigField(struct PlayerInfo *player,
13744                     int oldx, int oldy, int x, int y,
13745                     int real_dx, int real_dy, int mode)
13746 {
13747   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13748   boolean player_was_pushing = player->is_pushing;
13749   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13750   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13751   int jx = oldx, jy = oldy;
13752   int dx = x - jx, dy = y - jy;
13753   int nextx = x + dx, nexty = y + dy;
13754   int move_direction = (dx == -1 ? MV_LEFT  :
13755                         dx == +1 ? MV_RIGHT :
13756                         dy == -1 ? MV_UP    :
13757                         dy == +1 ? MV_DOWN  : MV_NONE);
13758   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13759   int dig_side = MV_DIR_OPPOSITE(move_direction);
13760   int old_element = Tile[jx][jy];
13761   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13762   int collect_count;
13763
13764   if (is_player)                // function can also be called by EL_PENGUIN
13765   {
13766     if (player->MovPos == 0)
13767     {
13768       player->is_digging = FALSE;
13769       player->is_collecting = FALSE;
13770     }
13771
13772     if (player->MovPos == 0)    // last pushing move finished
13773       player->is_pushing = FALSE;
13774
13775     if (mode == DF_NO_PUSH)     // player just stopped pushing
13776     {
13777       player->is_switching = FALSE;
13778       player->push_delay = -1;
13779
13780       return MP_NO_ACTION;
13781     }
13782   }
13783
13784   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13785     old_element = Back[jx][jy];
13786
13787   // in case of element dropped at player position, check background
13788   else if (Back[jx][jy] != EL_EMPTY &&
13789            game.engine_version >= VERSION_IDENT(2,2,0,0))
13790     old_element = Back[jx][jy];
13791
13792   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13793     return MP_NO_ACTION;        // field has no opening in this direction
13794
13795   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13796     return MP_NO_ACTION;        // field has no opening in this direction
13797
13798   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13799   {
13800     SplashAcid(x, y);
13801
13802     Tile[jx][jy] = player->artwork_element;
13803     InitMovingField(jx, jy, MV_DOWN);
13804     Store[jx][jy] = EL_ACID;
13805     ContinueMoving(jx, jy);
13806     BuryPlayer(player);
13807
13808     return MP_DONT_RUN_INTO;
13809   }
13810
13811   if (player_can_move && DONT_RUN_INTO(element))
13812   {
13813     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13814
13815     return MP_DONT_RUN_INTO;
13816   }
13817
13818   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13819     return MP_NO_ACTION;
13820
13821   collect_count = element_info[element].collect_count_initial;
13822
13823   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
13824     return MP_NO_ACTION;
13825
13826   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13827     player_can_move = player_can_move_or_snap;
13828
13829   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13830       game.engine_version >= VERSION_IDENT(2,2,0,0))
13831   {
13832     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13833                                player->index_bit, dig_side);
13834     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13835                                         player->index_bit, dig_side);
13836
13837     if (element == EL_DC_LANDMINE)
13838       Bang(x, y);
13839
13840     if (Tile[x][y] != element)          // field changed by snapping
13841       return MP_ACTION;
13842
13843     return MP_NO_ACTION;
13844   }
13845
13846   if (player->gravity && is_player && !player->is_auto_moving &&
13847       canFallDown(player) && move_direction != MV_DOWN &&
13848       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13849     return MP_NO_ACTION;        // player cannot walk here due to gravity
13850
13851   if (player_can_move &&
13852       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13853   {
13854     int sound_element = SND_ELEMENT(element);
13855     int sound_action = ACTION_WALKING;
13856
13857     if (IS_RND_GATE(element))
13858     {
13859       if (!player->key[RND_GATE_NR(element)])
13860         return MP_NO_ACTION;
13861     }
13862     else if (IS_RND_GATE_GRAY(element))
13863     {
13864       if (!player->key[RND_GATE_GRAY_NR(element)])
13865         return MP_NO_ACTION;
13866     }
13867     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13868     {
13869       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13870         return MP_NO_ACTION;
13871     }
13872     else if (element == EL_EXIT_OPEN ||
13873              element == EL_EM_EXIT_OPEN ||
13874              element == EL_EM_EXIT_OPENING ||
13875              element == EL_STEEL_EXIT_OPEN ||
13876              element == EL_EM_STEEL_EXIT_OPEN ||
13877              element == EL_EM_STEEL_EXIT_OPENING ||
13878              element == EL_SP_EXIT_OPEN ||
13879              element == EL_SP_EXIT_OPENING)
13880     {
13881       sound_action = ACTION_PASSING;    // player is passing exit
13882     }
13883     else if (element == EL_EMPTY)
13884     {
13885       sound_action = ACTION_MOVING;             // nothing to walk on
13886     }
13887
13888     // play sound from background or player, whatever is available
13889     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13890       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13891     else
13892       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13893   }
13894   else if (player_can_move &&
13895            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13896   {
13897     if (!ACCESS_FROM(element, opposite_direction))
13898       return MP_NO_ACTION;      // field not accessible from this direction
13899
13900     if (CAN_MOVE(element))      // only fixed elements can be passed!
13901       return MP_NO_ACTION;
13902
13903     if (IS_EM_GATE(element))
13904     {
13905       if (!player->key[EM_GATE_NR(element)])
13906         return MP_NO_ACTION;
13907     }
13908     else if (IS_EM_GATE_GRAY(element))
13909     {
13910       if (!player->key[EM_GATE_GRAY_NR(element)])
13911         return MP_NO_ACTION;
13912     }
13913     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13914     {
13915       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13916         return MP_NO_ACTION;
13917     }
13918     else if (IS_EMC_GATE(element))
13919     {
13920       if (!player->key[EMC_GATE_NR(element)])
13921         return MP_NO_ACTION;
13922     }
13923     else if (IS_EMC_GATE_GRAY(element))
13924     {
13925       if (!player->key[EMC_GATE_GRAY_NR(element)])
13926         return MP_NO_ACTION;
13927     }
13928     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13929     {
13930       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13931         return MP_NO_ACTION;
13932     }
13933     else if (element == EL_DC_GATE_WHITE ||
13934              element == EL_DC_GATE_WHITE_GRAY ||
13935              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13936     {
13937       if (player->num_white_keys == 0)
13938         return MP_NO_ACTION;
13939
13940       player->num_white_keys--;
13941     }
13942     else if (IS_SP_PORT(element))
13943     {
13944       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13945           element == EL_SP_GRAVITY_PORT_RIGHT ||
13946           element == EL_SP_GRAVITY_PORT_UP ||
13947           element == EL_SP_GRAVITY_PORT_DOWN)
13948         player->gravity = !player->gravity;
13949       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13950                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13951                element == EL_SP_GRAVITY_ON_PORT_UP ||
13952                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13953         player->gravity = TRUE;
13954       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13955                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13956                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13957                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13958         player->gravity = FALSE;
13959     }
13960
13961     // automatically move to the next field with double speed
13962     player->programmed_action = move_direction;
13963
13964     if (player->move_delay_reset_counter == 0)
13965     {
13966       player->move_delay_reset_counter = 2;     // two double speed steps
13967
13968       DOUBLE_PLAYER_SPEED(player);
13969     }
13970
13971     PlayLevelSoundAction(x, y, ACTION_PASSING);
13972   }
13973   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13974   {
13975     RemoveField(x, y);
13976
13977     if (mode != DF_SNAP)
13978     {
13979       GfxElement[x][y] = GFX_ELEMENT(element);
13980       player->is_digging = TRUE;
13981     }
13982
13983     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13984
13985     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13986                                         player->index_bit, dig_side);
13987
13988     if (mode == DF_SNAP)
13989     {
13990       if (level.block_snap_field)
13991         setFieldForSnapping(x, y, element, move_direction);
13992       else
13993         TestIfElementTouchesCustomElement(x, y);        // for empty space
13994
13995       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13996                                           player->index_bit, dig_side);
13997     }
13998   }
13999   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14000   {
14001     RemoveField(x, y);
14002
14003     if (is_player && mode != DF_SNAP)
14004     {
14005       GfxElement[x][y] = element;
14006       player->is_collecting = TRUE;
14007     }
14008
14009     if (element == EL_SPEED_PILL)
14010     {
14011       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14012     }
14013     else if (element == EL_EXTRA_TIME && level.time > 0)
14014     {
14015       TimeLeft += level.extra_time;
14016
14017       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14018
14019       DisplayGameControlValues();
14020     }
14021     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14022     {
14023       player->shield_normal_time_left += level.shield_normal_time;
14024       if (element == EL_SHIELD_DEADLY)
14025         player->shield_deadly_time_left += level.shield_deadly_time;
14026     }
14027     else if (element == EL_DYNAMITE ||
14028              element == EL_EM_DYNAMITE ||
14029              element == EL_SP_DISK_RED)
14030     {
14031       if (player->inventory_size < MAX_INVENTORY_SIZE)
14032         player->inventory_element[player->inventory_size++] = element;
14033
14034       DrawGameDoorValues();
14035     }
14036     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14037     {
14038       player->dynabomb_count++;
14039       player->dynabombs_left++;
14040     }
14041     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14042     {
14043       player->dynabomb_size++;
14044     }
14045     else if (element == EL_DYNABOMB_INCREASE_POWER)
14046     {
14047       player->dynabomb_xl = TRUE;
14048     }
14049     else if (IS_KEY(element))
14050     {
14051       player->key[KEY_NR(element)] = TRUE;
14052
14053       DrawGameDoorValues();
14054     }
14055     else if (element == EL_DC_KEY_WHITE)
14056     {
14057       player->num_white_keys++;
14058
14059       // display white keys?
14060       // DrawGameDoorValues();
14061     }
14062     else if (IS_ENVELOPE(element))
14063     {
14064       player->show_envelope = element;
14065     }
14066     else if (element == EL_EMC_LENSES)
14067     {
14068       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14069
14070       RedrawAllInvisibleElementsForLenses();
14071     }
14072     else if (element == EL_EMC_MAGNIFIER)
14073     {
14074       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14075
14076       RedrawAllInvisibleElementsForMagnifier();
14077     }
14078     else if (IS_DROPPABLE(element) ||
14079              IS_THROWABLE(element))     // can be collected and dropped
14080     {
14081       int i;
14082
14083       if (collect_count == 0)
14084         player->inventory_infinite_element = element;
14085       else
14086         for (i = 0; i < collect_count; i++)
14087           if (player->inventory_size < MAX_INVENTORY_SIZE)
14088             player->inventory_element[player->inventory_size++] = element;
14089
14090       DrawGameDoorValues();
14091     }
14092     else if (collect_count > 0)
14093     {
14094       game.gems_still_needed -= collect_count;
14095       if (game.gems_still_needed < 0)
14096         game.gems_still_needed = 0;
14097
14098       game.snapshot.collected_item = TRUE;
14099
14100       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14101
14102       DisplayGameControlValues();
14103     }
14104
14105     RaiseScoreElement(element);
14106     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14107
14108     if (is_player)
14109       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14110                                           player->index_bit, dig_side);
14111
14112     if (mode == DF_SNAP)
14113     {
14114       if (level.block_snap_field)
14115         setFieldForSnapping(x, y, element, move_direction);
14116       else
14117         TestIfElementTouchesCustomElement(x, y);        // for empty space
14118
14119       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14120                                           player->index_bit, dig_side);
14121     }
14122   }
14123   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14124   {
14125     if (mode == DF_SNAP && element != EL_BD_ROCK)
14126       return MP_NO_ACTION;
14127
14128     if (CAN_FALL(element) && dy)
14129       return MP_NO_ACTION;
14130
14131     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14132         !(element == EL_SPRING && level.use_spring_bug))
14133       return MP_NO_ACTION;
14134
14135     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14136         ((move_direction & MV_VERTICAL &&
14137           ((element_info[element].move_pattern & MV_LEFT &&
14138             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14139            (element_info[element].move_pattern & MV_RIGHT &&
14140             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14141          (move_direction & MV_HORIZONTAL &&
14142           ((element_info[element].move_pattern & MV_UP &&
14143             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14144            (element_info[element].move_pattern & MV_DOWN &&
14145             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14146       return MP_NO_ACTION;
14147
14148     // do not push elements already moving away faster than player
14149     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14150         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14151       return MP_NO_ACTION;
14152
14153     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14154     {
14155       if (player->push_delay_value == -1 || !player_was_pushing)
14156         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14157     }
14158     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14159     {
14160       if (player->push_delay_value == -1)
14161         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14162     }
14163     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14164     {
14165       if (!player->is_pushing)
14166         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14167     }
14168
14169     player->is_pushing = TRUE;
14170     player->is_active = TRUE;
14171
14172     if (!(IN_LEV_FIELD(nextx, nexty) &&
14173           (IS_FREE(nextx, nexty) ||
14174            (IS_SB_ELEMENT(element) &&
14175             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14176            (IS_CUSTOM_ELEMENT(element) &&
14177             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14178       return MP_NO_ACTION;
14179
14180     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14181       return MP_NO_ACTION;
14182
14183     if (player->push_delay == -1)       // new pushing; restart delay
14184       player->push_delay = 0;
14185
14186     if (player->push_delay < player->push_delay_value &&
14187         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14188         element != EL_SPRING && element != EL_BALLOON)
14189     {
14190       // make sure that there is no move delay before next try to push
14191       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14192         player->move_delay = 0;
14193
14194       return MP_NO_ACTION;
14195     }
14196
14197     if (IS_CUSTOM_ELEMENT(element) &&
14198         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14199     {
14200       if (!DigFieldByCE(nextx, nexty, element))
14201         return MP_NO_ACTION;
14202     }
14203
14204     if (IS_SB_ELEMENT(element))
14205     {
14206       boolean sokoban_task_solved = FALSE;
14207
14208       if (element == EL_SOKOBAN_FIELD_FULL)
14209       {
14210         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14211
14212         IncrementSokobanFieldsNeeded();
14213         IncrementSokobanObjectsNeeded();
14214       }
14215
14216       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14217       {
14218         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14219
14220         DecrementSokobanFieldsNeeded();
14221         DecrementSokobanObjectsNeeded();
14222
14223         // sokoban object was pushed from empty field to sokoban field
14224         if (Back[x][y] == EL_EMPTY)
14225           sokoban_task_solved = TRUE;
14226       }
14227
14228       Tile[x][y] = EL_SOKOBAN_OBJECT;
14229
14230       if (Back[x][y] == Back[nextx][nexty])
14231         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14232       else if (Back[x][y] != 0)
14233         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14234                                     ACTION_EMPTYING);
14235       else
14236         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14237                                     ACTION_FILLING);
14238
14239       if (sokoban_task_solved &&
14240           game.sokoban_fields_still_needed == 0 &&
14241           game.sokoban_objects_still_needed == 0 &&
14242           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14243       {
14244         game.players_still_needed = 0;
14245
14246         LevelSolved();
14247
14248         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14249       }
14250     }
14251     else
14252       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14253
14254     InitMovingField(x, y, move_direction);
14255     GfxAction[x][y] = ACTION_PUSHING;
14256
14257     if (mode == DF_SNAP)
14258       ContinueMoving(x, y);
14259     else
14260       MovPos[x][y] = (dx != 0 ? dx : dy);
14261
14262     Pushed[x][y] = TRUE;
14263     Pushed[nextx][nexty] = TRUE;
14264
14265     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14266       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14267     else
14268       player->push_delay_value = -1;    // get new value later
14269
14270     // check for element change _after_ element has been pushed
14271     if (game.use_change_when_pushing_bug)
14272     {
14273       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14274                                  player->index_bit, dig_side);
14275       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14276                                           player->index_bit, dig_side);
14277     }
14278   }
14279   else if (IS_SWITCHABLE(element))
14280   {
14281     if (PLAYER_SWITCHING(player, x, y))
14282     {
14283       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14284                                           player->index_bit, dig_side);
14285
14286       return MP_ACTION;
14287     }
14288
14289     player->is_switching = TRUE;
14290     player->switch_x = x;
14291     player->switch_y = y;
14292
14293     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14294
14295     if (element == EL_ROBOT_WHEEL)
14296     {
14297       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14298
14299       game.robot_wheel_x = x;
14300       game.robot_wheel_y = y;
14301       game.robot_wheel_active = TRUE;
14302
14303       TEST_DrawLevelField(x, y);
14304     }
14305     else if (element == EL_SP_TERMINAL)
14306     {
14307       int xx, yy;
14308
14309       SCAN_PLAYFIELD(xx, yy)
14310       {
14311         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14312         {
14313           Bang(xx, yy);
14314         }
14315         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14316         {
14317           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14318
14319           ResetGfxAnimation(xx, yy);
14320           TEST_DrawLevelField(xx, yy);
14321         }
14322       }
14323     }
14324     else if (IS_BELT_SWITCH(element))
14325     {
14326       ToggleBeltSwitch(x, y);
14327     }
14328     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14329              element == EL_SWITCHGATE_SWITCH_DOWN ||
14330              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14331              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14332     {
14333       ToggleSwitchgateSwitch(x, y);
14334     }
14335     else if (element == EL_LIGHT_SWITCH ||
14336              element == EL_LIGHT_SWITCH_ACTIVE)
14337     {
14338       ToggleLightSwitch(x, y);
14339     }
14340     else if (element == EL_TIMEGATE_SWITCH ||
14341              element == EL_DC_TIMEGATE_SWITCH)
14342     {
14343       ActivateTimegateSwitch(x, y);
14344     }
14345     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14346              element == EL_BALLOON_SWITCH_RIGHT ||
14347              element == EL_BALLOON_SWITCH_UP    ||
14348              element == EL_BALLOON_SWITCH_DOWN  ||
14349              element == EL_BALLOON_SWITCH_NONE  ||
14350              element == EL_BALLOON_SWITCH_ANY)
14351     {
14352       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14353                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14354                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14355                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14356                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14357                              move_direction);
14358     }
14359     else if (element == EL_LAMP)
14360     {
14361       Tile[x][y] = EL_LAMP_ACTIVE;
14362       game.lights_still_needed--;
14363
14364       ResetGfxAnimation(x, y);
14365       TEST_DrawLevelField(x, y);
14366     }
14367     else if (element == EL_TIME_ORB_FULL)
14368     {
14369       Tile[x][y] = EL_TIME_ORB_EMPTY;
14370
14371       if (level.time > 0 || level.use_time_orb_bug)
14372       {
14373         TimeLeft += level.time_orb_time;
14374         game.no_time_limit = FALSE;
14375
14376         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14377
14378         DisplayGameControlValues();
14379       }
14380
14381       ResetGfxAnimation(x, y);
14382       TEST_DrawLevelField(x, y);
14383     }
14384     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14385              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14386     {
14387       int xx, yy;
14388
14389       game.ball_active = !game.ball_active;
14390
14391       SCAN_PLAYFIELD(xx, yy)
14392       {
14393         int e = Tile[xx][yy];
14394
14395         if (game.ball_active)
14396         {
14397           if (e == EL_EMC_MAGIC_BALL)
14398             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14399           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14400             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14401         }
14402         else
14403         {
14404           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14405             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14406           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14407             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14408         }
14409       }
14410     }
14411
14412     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14413                                         player->index_bit, dig_side);
14414
14415     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14416                                         player->index_bit, dig_side);
14417
14418     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14419                                         player->index_bit, dig_side);
14420
14421     return MP_ACTION;
14422   }
14423   else
14424   {
14425     if (!PLAYER_SWITCHING(player, x, y))
14426     {
14427       player->is_switching = TRUE;
14428       player->switch_x = x;
14429       player->switch_y = y;
14430
14431       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14432                                  player->index_bit, dig_side);
14433       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14434                                           player->index_bit, dig_side);
14435
14436       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14437                                  player->index_bit, dig_side);
14438       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14439                                           player->index_bit, dig_side);
14440     }
14441
14442     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14443                                player->index_bit, dig_side);
14444     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14445                                         player->index_bit, dig_side);
14446
14447     return MP_NO_ACTION;
14448   }
14449
14450   player->push_delay = -1;
14451
14452   if (is_player)                // function can also be called by EL_PENGUIN
14453   {
14454     if (Tile[x][y] != element)          // really digged/collected something
14455     {
14456       player->is_collecting = !player->is_digging;
14457       player->is_active = TRUE;
14458     }
14459   }
14460
14461   return MP_MOVING;
14462 }
14463
14464 static boolean DigFieldByCE(int x, int y, int digging_element)
14465 {
14466   int element = Tile[x][y];
14467
14468   if (!IS_FREE(x, y))
14469   {
14470     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14471                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14472                   ACTION_BREAKING);
14473
14474     // no element can dig solid indestructible elements
14475     if (IS_INDESTRUCTIBLE(element) &&
14476         !IS_DIGGABLE(element) &&
14477         !IS_COLLECTIBLE(element))
14478       return FALSE;
14479
14480     if (AmoebaNr[x][y] &&
14481         (element == EL_AMOEBA_FULL ||
14482          element == EL_BD_AMOEBA ||
14483          element == EL_AMOEBA_GROWING))
14484     {
14485       AmoebaCnt[AmoebaNr[x][y]]--;
14486       AmoebaCnt2[AmoebaNr[x][y]]--;
14487     }
14488
14489     if (IS_MOVING(x, y))
14490       RemoveMovingField(x, y);
14491     else
14492     {
14493       RemoveField(x, y);
14494       TEST_DrawLevelField(x, y);
14495     }
14496
14497     // if digged element was about to explode, prevent the explosion
14498     ExplodeField[x][y] = EX_TYPE_NONE;
14499
14500     PlayLevelSoundAction(x, y, action);
14501   }
14502
14503   Store[x][y] = EL_EMPTY;
14504
14505   // this makes it possible to leave the removed element again
14506   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14507     Store[x][y] = element;
14508
14509   return TRUE;
14510 }
14511
14512 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14513 {
14514   int jx = player->jx, jy = player->jy;
14515   int x = jx + dx, y = jy + dy;
14516   int snap_direction = (dx == -1 ? MV_LEFT  :
14517                         dx == +1 ? MV_RIGHT :
14518                         dy == -1 ? MV_UP    :
14519                         dy == +1 ? MV_DOWN  : MV_NONE);
14520   boolean can_continue_snapping = (level.continuous_snapping &&
14521                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14522
14523   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14524     return FALSE;
14525
14526   if (!player->active || !IN_LEV_FIELD(x, y))
14527     return FALSE;
14528
14529   if (dx && dy)
14530     return FALSE;
14531
14532   if (!dx && !dy)
14533   {
14534     if (player->MovPos == 0)
14535       player->is_pushing = FALSE;
14536
14537     player->is_snapping = FALSE;
14538
14539     if (player->MovPos == 0)
14540     {
14541       player->is_moving = FALSE;
14542       player->is_digging = FALSE;
14543       player->is_collecting = FALSE;
14544     }
14545
14546     return FALSE;
14547   }
14548
14549   // prevent snapping with already pressed snap key when not allowed
14550   if (player->is_snapping && !can_continue_snapping)
14551     return FALSE;
14552
14553   player->MovDir = snap_direction;
14554
14555   if (player->MovPos == 0)
14556   {
14557     player->is_moving = FALSE;
14558     player->is_digging = FALSE;
14559     player->is_collecting = FALSE;
14560   }
14561
14562   player->is_dropping = FALSE;
14563   player->is_dropping_pressed = FALSE;
14564   player->drop_pressed_delay = 0;
14565
14566   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14567     return FALSE;
14568
14569   player->is_snapping = TRUE;
14570   player->is_active = TRUE;
14571
14572   if (player->MovPos == 0)
14573   {
14574     player->is_moving = FALSE;
14575     player->is_digging = FALSE;
14576     player->is_collecting = FALSE;
14577   }
14578
14579   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14580     TEST_DrawLevelField(player->last_jx, player->last_jy);
14581
14582   TEST_DrawLevelField(x, y);
14583
14584   return TRUE;
14585 }
14586
14587 static boolean DropElement(struct PlayerInfo *player)
14588 {
14589   int old_element, new_element;
14590   int dropx = player->jx, dropy = player->jy;
14591   int drop_direction = player->MovDir;
14592   int drop_side = drop_direction;
14593   int drop_element = get_next_dropped_element(player);
14594
14595   /* do not drop an element on top of another element; when holding drop key
14596      pressed without moving, dropped element must move away before the next
14597      element can be dropped (this is especially important if the next element
14598      is dynamite, which can be placed on background for historical reasons) */
14599   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
14600     return MP_ACTION;
14601
14602   if (IS_THROWABLE(drop_element))
14603   {
14604     dropx += GET_DX_FROM_DIR(drop_direction);
14605     dropy += GET_DY_FROM_DIR(drop_direction);
14606
14607     if (!IN_LEV_FIELD(dropx, dropy))
14608       return FALSE;
14609   }
14610
14611   old_element = Tile[dropx][dropy];     // old element at dropping position
14612   new_element = drop_element;           // default: no change when dropping
14613
14614   // check if player is active, not moving and ready to drop
14615   if (!player->active || player->MovPos || player->drop_delay > 0)
14616     return FALSE;
14617
14618   // check if player has anything that can be dropped
14619   if (new_element == EL_UNDEFINED)
14620     return FALSE;
14621
14622   // only set if player has anything that can be dropped
14623   player->is_dropping_pressed = TRUE;
14624
14625   // check if drop key was pressed long enough for EM style dynamite
14626   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14627     return FALSE;
14628
14629   // check if anything can be dropped at the current position
14630   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14631     return FALSE;
14632
14633   // collected custom elements can only be dropped on empty fields
14634   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14635     return FALSE;
14636
14637   if (old_element != EL_EMPTY)
14638     Back[dropx][dropy] = old_element;   // store old element on this field
14639
14640   ResetGfxAnimation(dropx, dropy);
14641   ResetRandomAnimationValue(dropx, dropy);
14642
14643   if (player->inventory_size > 0 ||
14644       player->inventory_infinite_element != EL_UNDEFINED)
14645   {
14646     if (player->inventory_size > 0)
14647     {
14648       player->inventory_size--;
14649
14650       DrawGameDoorValues();
14651
14652       if (new_element == EL_DYNAMITE)
14653         new_element = EL_DYNAMITE_ACTIVE;
14654       else if (new_element == EL_EM_DYNAMITE)
14655         new_element = EL_EM_DYNAMITE_ACTIVE;
14656       else if (new_element == EL_SP_DISK_RED)
14657         new_element = EL_SP_DISK_RED_ACTIVE;
14658     }
14659
14660     Tile[dropx][dropy] = new_element;
14661
14662     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14663       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14664                           el2img(Tile[dropx][dropy]), 0);
14665
14666     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14667
14668     // needed if previous element just changed to "empty" in the last frame
14669     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14670
14671     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14672                                player->index_bit, drop_side);
14673     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14674                                         CE_PLAYER_DROPS_X,
14675                                         player->index_bit, drop_side);
14676
14677     TestIfElementTouchesCustomElement(dropx, dropy);
14678   }
14679   else          // player is dropping a dyna bomb
14680   {
14681     player->dynabombs_left--;
14682
14683     Tile[dropx][dropy] = new_element;
14684
14685     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14686       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14687                           el2img(Tile[dropx][dropy]), 0);
14688
14689     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14690   }
14691
14692   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
14693     InitField_WithBug1(dropx, dropy, FALSE);
14694
14695   new_element = Tile[dropx][dropy];     // element might have changed
14696
14697   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14698       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14699   {
14700     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14701       MovDir[dropx][dropy] = drop_direction;
14702
14703     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14704
14705     // do not cause impact style collision by dropping elements that can fall
14706     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14707   }
14708
14709   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14710   player->is_dropping = TRUE;
14711
14712   player->drop_pressed_delay = 0;
14713   player->is_dropping_pressed = FALSE;
14714
14715   player->drop_x = dropx;
14716   player->drop_y = dropy;
14717
14718   return TRUE;
14719 }
14720
14721 // ----------------------------------------------------------------------------
14722 // game sound playing functions
14723 // ----------------------------------------------------------------------------
14724
14725 static int *loop_sound_frame = NULL;
14726 static int *loop_sound_volume = NULL;
14727
14728 void InitPlayLevelSound(void)
14729 {
14730   int num_sounds = getSoundListSize();
14731
14732   checked_free(loop_sound_frame);
14733   checked_free(loop_sound_volume);
14734
14735   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14736   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14737 }
14738
14739 static void PlayLevelSound(int x, int y, int nr)
14740 {
14741   int sx = SCREENX(x), sy = SCREENY(y);
14742   int volume, stereo_position;
14743   int max_distance = 8;
14744   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14745
14746   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14747       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14748     return;
14749
14750   if (!IN_LEV_FIELD(x, y) ||
14751       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14752       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14753     return;
14754
14755   volume = SOUND_MAX_VOLUME;
14756
14757   if (!IN_SCR_FIELD(sx, sy))
14758   {
14759     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14760     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14761
14762     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14763   }
14764
14765   stereo_position = (SOUND_MAX_LEFT +
14766                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14767                      (SCR_FIELDX + 2 * max_distance));
14768
14769   if (IS_LOOP_SOUND(nr))
14770   {
14771     /* This assures that quieter loop sounds do not overwrite louder ones,
14772        while restarting sound volume comparison with each new game frame. */
14773
14774     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14775       return;
14776
14777     loop_sound_volume[nr] = volume;
14778     loop_sound_frame[nr] = FrameCounter;
14779   }
14780
14781   PlaySoundExt(nr, volume, stereo_position, type);
14782 }
14783
14784 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14785 {
14786   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14787                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14788                  y < LEVELY(BY1) ? LEVELY(BY1) :
14789                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14790                  sound_action);
14791 }
14792
14793 static void PlayLevelSoundAction(int x, int y, int action)
14794 {
14795   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
14796 }
14797
14798 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14799 {
14800   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14801
14802   if (sound_effect != SND_UNDEFINED)
14803     PlayLevelSound(x, y, sound_effect);
14804 }
14805
14806 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14807                                               int action)
14808 {
14809   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14810
14811   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14812     PlayLevelSound(x, y, sound_effect);
14813 }
14814
14815 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14816 {
14817   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
14818
14819   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14820     PlayLevelSound(x, y, sound_effect);
14821 }
14822
14823 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14824 {
14825   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
14826
14827   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14828     StopSound(sound_effect);
14829 }
14830
14831 static int getLevelMusicNr(void)
14832 {
14833   if (levelset.music[level_nr] != MUS_UNDEFINED)
14834     return levelset.music[level_nr];            // from config file
14835   else
14836     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
14837 }
14838
14839 static void FadeLevelSounds(void)
14840 {
14841   FadeSounds();
14842 }
14843
14844 static void FadeLevelMusic(void)
14845 {
14846   int music_nr = getLevelMusicNr();
14847   char *curr_music = getCurrentlyPlayingMusicFilename();
14848   char *next_music = getMusicInfoEntryFilename(music_nr);
14849
14850   if (!strEqual(curr_music, next_music))
14851     FadeMusic();
14852 }
14853
14854 void FadeLevelSoundsAndMusic(void)
14855 {
14856   FadeLevelSounds();
14857   FadeLevelMusic();
14858 }
14859
14860 static void PlayLevelMusic(void)
14861 {
14862   int music_nr = getLevelMusicNr();
14863   char *curr_music = getCurrentlyPlayingMusicFilename();
14864   char *next_music = getMusicInfoEntryFilename(music_nr);
14865
14866   if (!strEqual(curr_music, next_music))
14867     PlayMusicLoop(music_nr);
14868 }
14869
14870 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14871 {
14872   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
14873   int offset = 0;
14874   int x = xx - offset;
14875   int y = yy - offset;
14876
14877   switch (sample)
14878   {
14879     case SOUND_blank:
14880       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14881       break;
14882
14883     case SOUND_roll:
14884       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14885       break;
14886
14887     case SOUND_stone:
14888       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14889       break;
14890
14891     case SOUND_nut:
14892       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14893       break;
14894
14895     case SOUND_crack:
14896       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14897       break;
14898
14899     case SOUND_bug:
14900       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14901       break;
14902
14903     case SOUND_tank:
14904       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14905       break;
14906
14907     case SOUND_android_clone:
14908       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14909       break;
14910
14911     case SOUND_android_move:
14912       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14913       break;
14914
14915     case SOUND_spring:
14916       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14917       break;
14918
14919     case SOUND_slurp:
14920       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14921       break;
14922
14923     case SOUND_eater:
14924       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14925       break;
14926
14927     case SOUND_eater_eat:
14928       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14929       break;
14930
14931     case SOUND_alien:
14932       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14933       break;
14934
14935     case SOUND_collect:
14936       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14937       break;
14938
14939     case SOUND_diamond:
14940       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14941       break;
14942
14943     case SOUND_squash:
14944       // !!! CHECK THIS !!!
14945 #if 1
14946       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14947 #else
14948       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14949 #endif
14950       break;
14951
14952     case SOUND_wonderfall:
14953       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14954       break;
14955
14956     case SOUND_drip:
14957       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14958       break;
14959
14960     case SOUND_push:
14961       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14962       break;
14963
14964     case SOUND_dirt:
14965       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14966       break;
14967
14968     case SOUND_acid:
14969       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14970       break;
14971
14972     case SOUND_ball:
14973       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14974       break;
14975
14976     case SOUND_slide:
14977       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14978       break;
14979
14980     case SOUND_wonder:
14981       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14982       break;
14983
14984     case SOUND_door:
14985       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14986       break;
14987
14988     case SOUND_exit_open:
14989       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14990       break;
14991
14992     case SOUND_exit_leave:
14993       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14994       break;
14995
14996     case SOUND_dynamite:
14997       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14998       break;
14999
15000     case SOUND_tick:
15001       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15002       break;
15003
15004     case SOUND_press:
15005       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15006       break;
15007
15008     case SOUND_wheel:
15009       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15010       break;
15011
15012     case SOUND_boom:
15013       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15014       break;
15015
15016     case SOUND_die:
15017       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15018       break;
15019
15020     case SOUND_time:
15021       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15022       break;
15023
15024     default:
15025       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15026       break;
15027   }
15028 }
15029
15030 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15031 {
15032   int element = map_element_SP_to_RND(element_sp);
15033   int action = map_action_SP_to_RND(action_sp);
15034   int offset = (setup.sp_show_border_elements ? 0 : 1);
15035   int x = xx - offset;
15036   int y = yy - offset;
15037
15038   PlayLevelSoundElementAction(x, y, element, action);
15039 }
15040
15041 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15042 {
15043   int element = map_element_MM_to_RND(element_mm);
15044   int action = map_action_MM_to_RND(action_mm);
15045   int offset = 0;
15046   int x = xx - offset;
15047   int y = yy - offset;
15048
15049   if (!IS_MM_ELEMENT(element))
15050     element = EL_MM_DEFAULT;
15051
15052   PlayLevelSoundElementAction(x, y, element, action);
15053 }
15054
15055 void PlaySound_MM(int sound_mm)
15056 {
15057   int sound = map_sound_MM_to_RND(sound_mm);
15058
15059   if (sound == SND_UNDEFINED)
15060     return;
15061
15062   PlaySound(sound);
15063 }
15064
15065 void PlaySoundLoop_MM(int sound_mm)
15066 {
15067   int sound = map_sound_MM_to_RND(sound_mm);
15068
15069   if (sound == SND_UNDEFINED)
15070     return;
15071
15072   PlaySoundLoop(sound);
15073 }
15074
15075 void StopSound_MM(int sound_mm)
15076 {
15077   int sound = map_sound_MM_to_RND(sound_mm);
15078
15079   if (sound == SND_UNDEFINED)
15080     return;
15081
15082   StopSound(sound);
15083 }
15084
15085 void RaiseScore(int value)
15086 {
15087   game.score += value;
15088
15089   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15090
15091   DisplayGameControlValues();
15092 }
15093
15094 void RaiseScoreElement(int element)
15095 {
15096   switch (element)
15097   {
15098     case EL_EMERALD:
15099     case EL_BD_DIAMOND:
15100     case EL_EMERALD_YELLOW:
15101     case EL_EMERALD_RED:
15102     case EL_EMERALD_PURPLE:
15103     case EL_SP_INFOTRON:
15104       RaiseScore(level.score[SC_EMERALD]);
15105       break;
15106     case EL_DIAMOND:
15107       RaiseScore(level.score[SC_DIAMOND]);
15108       break;
15109     case EL_CRYSTAL:
15110       RaiseScore(level.score[SC_CRYSTAL]);
15111       break;
15112     case EL_PEARL:
15113       RaiseScore(level.score[SC_PEARL]);
15114       break;
15115     case EL_BUG:
15116     case EL_BD_BUTTERFLY:
15117     case EL_SP_ELECTRON:
15118       RaiseScore(level.score[SC_BUG]);
15119       break;
15120     case EL_SPACESHIP:
15121     case EL_BD_FIREFLY:
15122     case EL_SP_SNIKSNAK:
15123       RaiseScore(level.score[SC_SPACESHIP]);
15124       break;
15125     case EL_YAMYAM:
15126     case EL_DARK_YAMYAM:
15127       RaiseScore(level.score[SC_YAMYAM]);
15128       break;
15129     case EL_ROBOT:
15130       RaiseScore(level.score[SC_ROBOT]);
15131       break;
15132     case EL_PACMAN:
15133       RaiseScore(level.score[SC_PACMAN]);
15134       break;
15135     case EL_NUT:
15136       RaiseScore(level.score[SC_NUT]);
15137       break;
15138     case EL_DYNAMITE:
15139     case EL_EM_DYNAMITE:
15140     case EL_SP_DISK_RED:
15141     case EL_DYNABOMB_INCREASE_NUMBER:
15142     case EL_DYNABOMB_INCREASE_SIZE:
15143     case EL_DYNABOMB_INCREASE_POWER:
15144       RaiseScore(level.score[SC_DYNAMITE]);
15145       break;
15146     case EL_SHIELD_NORMAL:
15147     case EL_SHIELD_DEADLY:
15148       RaiseScore(level.score[SC_SHIELD]);
15149       break;
15150     case EL_EXTRA_TIME:
15151       RaiseScore(level.extra_time_score);
15152       break;
15153     case EL_KEY_1:
15154     case EL_KEY_2:
15155     case EL_KEY_3:
15156     case EL_KEY_4:
15157     case EL_EM_KEY_1:
15158     case EL_EM_KEY_2:
15159     case EL_EM_KEY_3:
15160     case EL_EM_KEY_4:
15161     case EL_EMC_KEY_5:
15162     case EL_EMC_KEY_6:
15163     case EL_EMC_KEY_7:
15164     case EL_EMC_KEY_8:
15165     case EL_DC_KEY_WHITE:
15166       RaiseScore(level.score[SC_KEY]);
15167       break;
15168     default:
15169       RaiseScore(element_info[element].collect_score);
15170       break;
15171   }
15172 }
15173
15174 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15175 {
15176   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15177   {
15178     // closing door required in case of envelope style request dialogs
15179     if (!skip_request)
15180     {
15181       // prevent short reactivation of overlay buttons while closing door
15182       SetOverlayActive(FALSE);
15183
15184       CloseDoor(DOOR_CLOSE_1);
15185     }
15186
15187     if (network.enabled)
15188       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15189     else
15190     {
15191       if (quick_quit)
15192         FadeSkipNextFadeIn();
15193
15194       SetGameStatus(GAME_MODE_MAIN);
15195
15196       DrawMainMenu();
15197     }
15198   }
15199   else          // continue playing the game
15200   {
15201     if (tape.playing && tape.deactivate_display)
15202       TapeDeactivateDisplayOff(TRUE);
15203
15204     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15205
15206     if (tape.playing && tape.deactivate_display)
15207       TapeDeactivateDisplayOn();
15208   }
15209 }
15210
15211 void RequestQuitGame(boolean ask_if_really_quit)
15212 {
15213   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15214   boolean skip_request = game.all_players_gone || quick_quit;
15215
15216   RequestQuitGameExt(skip_request, quick_quit,
15217                      "Do you really want to quit the game?");
15218 }
15219
15220 void RequestRestartGame(char *message)
15221 {
15222   game.restart_game_message = NULL;
15223
15224   boolean has_started_game = hasStartedNetworkGame();
15225   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15226
15227   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15228   {
15229     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15230   }
15231   else
15232   {
15233     SetGameStatus(GAME_MODE_MAIN);
15234
15235     DrawMainMenu();
15236   }
15237 }
15238
15239 void CheckGameOver(void)
15240 {
15241   static boolean last_game_over = FALSE;
15242   static int game_over_delay = 0;
15243   int game_over_delay_value = 50;
15244   boolean game_over = checkGameFailed();
15245
15246   // do not handle game over if request dialog is already active
15247   if (game.request_active)
15248     return;
15249
15250   // do not ask to play again if game was never actually played
15251   if (!game.GamePlayed)
15252     return;
15253
15254   if (!game_over)
15255   {
15256     last_game_over = FALSE;
15257     game_over_delay = game_over_delay_value;
15258
15259     return;
15260   }
15261
15262   if (game_over_delay > 0)
15263   {
15264     game_over_delay--;
15265
15266     return;
15267   }
15268
15269   if (last_game_over != game_over)
15270     game.restart_game_message = (hasStartedNetworkGame() ?
15271                                  "Game over! Play it again?" :
15272                                  "Game over!");
15273
15274   last_game_over = game_over;
15275 }
15276
15277 boolean checkGameSolved(void)
15278 {
15279   // set for all game engines if level was solved
15280   return game.LevelSolved_GameEnd;
15281 }
15282
15283 boolean checkGameFailed(void)
15284 {
15285   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15286     return (game_em.game_over && !game_em.level_solved);
15287   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15288     return (game_sp.game_over && !game_sp.level_solved);
15289   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15290     return (game_mm.game_over && !game_mm.level_solved);
15291   else                          // GAME_ENGINE_TYPE_RND
15292     return (game.GameOver && !game.LevelSolved);
15293 }
15294
15295 boolean checkGameEnded(void)
15296 {
15297   return (checkGameSolved() || checkGameFailed());
15298 }
15299
15300
15301 // ----------------------------------------------------------------------------
15302 // random generator functions
15303 // ----------------------------------------------------------------------------
15304
15305 unsigned int InitEngineRandom_RND(int seed)
15306 {
15307   game.num_random_calls = 0;
15308
15309   return InitEngineRandom(seed);
15310 }
15311
15312 unsigned int RND(int max)
15313 {
15314   if (max > 0)
15315   {
15316     game.num_random_calls++;
15317
15318     return GetEngineRandom(max);
15319   }
15320
15321   return 0;
15322 }
15323
15324
15325 // ----------------------------------------------------------------------------
15326 // game engine snapshot handling functions
15327 // ----------------------------------------------------------------------------
15328
15329 struct EngineSnapshotInfo
15330 {
15331   // runtime values for custom element collect score
15332   int collect_score[NUM_CUSTOM_ELEMENTS];
15333
15334   // runtime values for group element choice position
15335   int choice_pos[NUM_GROUP_ELEMENTS];
15336
15337   // runtime values for belt position animations
15338   int belt_graphic[4][NUM_BELT_PARTS];
15339   int belt_anim_mode[4][NUM_BELT_PARTS];
15340 };
15341
15342 static struct EngineSnapshotInfo engine_snapshot_rnd;
15343 static char *snapshot_level_identifier = NULL;
15344 static int snapshot_level_nr = -1;
15345
15346 static void SaveEngineSnapshotValues_RND(void)
15347 {
15348   static int belt_base_active_element[4] =
15349   {
15350     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15351     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15352     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15353     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15354   };
15355   int i, j;
15356
15357   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15358   {
15359     int element = EL_CUSTOM_START + i;
15360
15361     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15362   }
15363
15364   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15365   {
15366     int element = EL_GROUP_START + i;
15367
15368     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15369   }
15370
15371   for (i = 0; i < 4; i++)
15372   {
15373     for (j = 0; j < NUM_BELT_PARTS; j++)
15374     {
15375       int element = belt_base_active_element[i] + j;
15376       int graphic = el2img(element);
15377       int anim_mode = graphic_info[graphic].anim_mode;
15378
15379       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15380       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15381     }
15382   }
15383 }
15384
15385 static void LoadEngineSnapshotValues_RND(void)
15386 {
15387   unsigned int num_random_calls = game.num_random_calls;
15388   int i, j;
15389
15390   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15391   {
15392     int element = EL_CUSTOM_START + i;
15393
15394     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15395   }
15396
15397   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15398   {
15399     int element = EL_GROUP_START + i;
15400
15401     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15402   }
15403
15404   for (i = 0; i < 4; i++)
15405   {
15406     for (j = 0; j < NUM_BELT_PARTS; j++)
15407     {
15408       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15409       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15410
15411       graphic_info[graphic].anim_mode = anim_mode;
15412     }
15413   }
15414
15415   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15416   {
15417     InitRND(tape.random_seed);
15418     for (i = 0; i < num_random_calls; i++)
15419       RND(1);
15420   }
15421
15422   if (game.num_random_calls != num_random_calls)
15423   {
15424     Error(ERR_INFO, "number of random calls out of sync");
15425     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15426     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15427     Error(ERR_EXIT, "this should not happen -- please debug");
15428   }
15429 }
15430
15431 void FreeEngineSnapshotSingle(void)
15432 {
15433   FreeSnapshotSingle();
15434
15435   setString(&snapshot_level_identifier, NULL);
15436   snapshot_level_nr = -1;
15437 }
15438
15439 void FreeEngineSnapshotList(void)
15440 {
15441   FreeSnapshotList();
15442 }
15443
15444 static ListNode *SaveEngineSnapshotBuffers(void)
15445 {
15446   ListNode *buffers = NULL;
15447
15448   // copy some special values to a structure better suited for the snapshot
15449
15450   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15451     SaveEngineSnapshotValues_RND();
15452   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15453     SaveEngineSnapshotValues_EM();
15454   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15455     SaveEngineSnapshotValues_SP(&buffers);
15456   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15457     SaveEngineSnapshotValues_MM(&buffers);
15458
15459   // save values stored in special snapshot structure
15460
15461   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15462     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15463   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15464     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15465   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15466     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15467   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15468     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15469
15470   // save further RND engine values
15471
15472   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15473   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15474   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15475
15476   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15477   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15478   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15479   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15480   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15481
15482   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15483   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15484   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15485
15486   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15487
15488   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15489   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15490
15491   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15492   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15493   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15494   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15495   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15496   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15497   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15498   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15499   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15500   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15501   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15502   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15503   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15504   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15505   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15506   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15507   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15508   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15509
15510   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15511   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15512
15513   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15514   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15515   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15516
15517   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15518   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15519
15520   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15521   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15522   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15523   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15524   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15525
15526   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15527   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15528
15529 #if 0
15530   ListNode *node = engine_snapshot_list_rnd;
15531   int num_bytes = 0;
15532
15533   while (node != NULL)
15534   {
15535     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15536
15537     node = node->next;
15538   }
15539
15540   Debug("game:playing:SaveEngineSnapshotBuffers",
15541         "size of engine snapshot: %d bytes", num_bytes);
15542 #endif
15543
15544   return buffers;
15545 }
15546
15547 void SaveEngineSnapshotSingle(void)
15548 {
15549   ListNode *buffers = SaveEngineSnapshotBuffers();
15550
15551   // finally save all snapshot buffers to single snapshot
15552   SaveSnapshotSingle(buffers);
15553
15554   // save level identification information
15555   setString(&snapshot_level_identifier, leveldir_current->identifier);
15556   snapshot_level_nr = level_nr;
15557 }
15558
15559 boolean CheckSaveEngineSnapshotToList(void)
15560 {
15561   boolean save_snapshot =
15562     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15563      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15564       game.snapshot.changed_action) ||
15565      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15566       game.snapshot.collected_item));
15567
15568   game.snapshot.changed_action = FALSE;
15569   game.snapshot.collected_item = FALSE;
15570   game.snapshot.save_snapshot = save_snapshot;
15571
15572   return save_snapshot;
15573 }
15574
15575 void SaveEngineSnapshotToList(void)
15576 {
15577   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15578       tape.quick_resume)
15579     return;
15580
15581   ListNode *buffers = SaveEngineSnapshotBuffers();
15582
15583   // finally save all snapshot buffers to snapshot list
15584   SaveSnapshotToList(buffers);
15585 }
15586
15587 void SaveEngineSnapshotToListInitial(void)
15588 {
15589   FreeEngineSnapshotList();
15590
15591   SaveEngineSnapshotToList();
15592 }
15593
15594 static void LoadEngineSnapshotValues(void)
15595 {
15596   // restore special values from snapshot structure
15597
15598   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15599     LoadEngineSnapshotValues_RND();
15600   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15601     LoadEngineSnapshotValues_EM();
15602   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15603     LoadEngineSnapshotValues_SP();
15604   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15605     LoadEngineSnapshotValues_MM();
15606 }
15607
15608 void LoadEngineSnapshotSingle(void)
15609 {
15610   LoadSnapshotSingle();
15611
15612   LoadEngineSnapshotValues();
15613 }
15614
15615 static void LoadEngineSnapshot_Undo(int steps)
15616 {
15617   LoadSnapshotFromList_Older(steps);
15618
15619   LoadEngineSnapshotValues();
15620 }
15621
15622 static void LoadEngineSnapshot_Redo(int steps)
15623 {
15624   LoadSnapshotFromList_Newer(steps);
15625
15626   LoadEngineSnapshotValues();
15627 }
15628
15629 boolean CheckEngineSnapshotSingle(void)
15630 {
15631   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15632           snapshot_level_nr == level_nr);
15633 }
15634
15635 boolean CheckEngineSnapshotList(void)
15636 {
15637   return CheckSnapshotList();
15638 }
15639
15640
15641 // ---------- new game button stuff -------------------------------------------
15642
15643 static struct
15644 {
15645   int graphic;
15646   struct XY *pos;
15647   int gadget_id;
15648   boolean *setup_value;
15649   boolean allowed_on_tape;
15650   boolean is_touch_button;
15651   char *infotext;
15652 } gamebutton_info[NUM_GAME_BUTTONS] =
15653 {
15654   {
15655     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15656     GAME_CTRL_ID_STOP,                          NULL,
15657     TRUE, FALSE,                                "stop game"
15658   },
15659   {
15660     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15661     GAME_CTRL_ID_PAUSE,                         NULL,
15662     TRUE, FALSE,                                "pause game"
15663   },
15664   {
15665     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15666     GAME_CTRL_ID_PLAY,                          NULL,
15667     TRUE, FALSE,                                "play game"
15668   },
15669   {
15670     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15671     GAME_CTRL_ID_UNDO,                          NULL,
15672     TRUE, FALSE,                                "undo step"
15673   },
15674   {
15675     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15676     GAME_CTRL_ID_REDO,                          NULL,
15677     TRUE, FALSE,                                "redo step"
15678   },
15679   {
15680     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15681     GAME_CTRL_ID_SAVE,                          NULL,
15682     TRUE, FALSE,                                "save game"
15683   },
15684   {
15685     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15686     GAME_CTRL_ID_PAUSE2,                        NULL,
15687     TRUE, FALSE,                                "pause game"
15688   },
15689   {
15690     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15691     GAME_CTRL_ID_LOAD,                          NULL,
15692     TRUE, FALSE,                                "load game"
15693   },
15694   {
15695     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15696     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15697     FALSE, FALSE,                               "stop game"
15698   },
15699   {
15700     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15701     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15702     FALSE, FALSE,                               "pause game"
15703   },
15704   {
15705     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15706     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15707     FALSE, FALSE,                               "play game"
15708   },
15709   {
15710     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
15711     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
15712     FALSE, TRUE,                                "stop game"
15713   },
15714   {
15715     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
15716     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
15717     FALSE, TRUE,                                "pause game"
15718   },
15719   {
15720     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15721     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15722     TRUE, FALSE,                                "background music on/off"
15723   },
15724   {
15725     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15726     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15727     TRUE, FALSE,                                "sound loops on/off"
15728   },
15729   {
15730     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15731     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15732     TRUE, FALSE,                                "normal sounds on/off"
15733   },
15734   {
15735     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15736     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15737     FALSE, FALSE,                               "background music on/off"
15738   },
15739   {
15740     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15741     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15742     FALSE, FALSE,                               "sound loops on/off"
15743   },
15744   {
15745     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15746     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15747     FALSE, FALSE,                               "normal sounds on/off"
15748   }
15749 };
15750
15751 void CreateGameButtons(void)
15752 {
15753   int i;
15754
15755   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15756   {
15757     int graphic = gamebutton_info[i].graphic;
15758     struct GraphicInfo *gfx = &graphic_info[graphic];
15759     struct XY *pos = gamebutton_info[i].pos;
15760     struct GadgetInfo *gi;
15761     int button_type;
15762     boolean checked;
15763     unsigned int event_mask;
15764     boolean is_touch_button = gamebutton_info[i].is_touch_button;
15765     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15766     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15767     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
15768     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
15769     int gd_x   = gfx->src_x;
15770     int gd_y   = gfx->src_y;
15771     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15772     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15773     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15774     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15775     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15776     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15777     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
15778     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
15779     int id = i;
15780
15781     if (gfx->bitmap == NULL)
15782     {
15783       game_gadget[id] = NULL;
15784
15785       continue;
15786     }
15787
15788     if (id == GAME_CTRL_ID_STOP ||
15789         id == GAME_CTRL_ID_PANEL_STOP ||
15790         id == GAME_CTRL_ID_TOUCH_STOP ||
15791         id == GAME_CTRL_ID_PLAY ||
15792         id == GAME_CTRL_ID_PANEL_PLAY ||
15793         id == GAME_CTRL_ID_SAVE ||
15794         id == GAME_CTRL_ID_LOAD)
15795     {
15796       button_type = GD_TYPE_NORMAL_BUTTON;
15797       checked = FALSE;
15798       event_mask = GD_EVENT_RELEASED;
15799     }
15800     else if (id == GAME_CTRL_ID_UNDO ||
15801              id == GAME_CTRL_ID_REDO)
15802     {
15803       button_type = GD_TYPE_NORMAL_BUTTON;
15804       checked = FALSE;
15805       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15806     }
15807     else
15808     {
15809       button_type = GD_TYPE_CHECK_BUTTON;
15810       checked = (gamebutton_info[i].setup_value != NULL ?
15811                  *gamebutton_info[i].setup_value : FALSE);
15812       event_mask = GD_EVENT_PRESSED;
15813     }
15814
15815     gi = CreateGadget(GDI_CUSTOM_ID, id,
15816                       GDI_IMAGE_ID, graphic,
15817                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15818                       GDI_X, base_x + x,
15819                       GDI_Y, base_y + y,
15820                       GDI_WIDTH, gfx->width,
15821                       GDI_HEIGHT, gfx->height,
15822                       GDI_TYPE, button_type,
15823                       GDI_STATE, GD_BUTTON_UNPRESSED,
15824                       GDI_CHECKED, checked,
15825                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15826                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15827                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15828                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15829                       GDI_DIRECT_DRAW, FALSE,
15830                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
15831                       GDI_EVENT_MASK, event_mask,
15832                       GDI_CALLBACK_ACTION, HandleGameButtons,
15833                       GDI_END);
15834
15835     if (gi == NULL)
15836       Error(ERR_EXIT, "cannot create gadget");
15837
15838     game_gadget[id] = gi;
15839   }
15840 }
15841
15842 void FreeGameButtons(void)
15843 {
15844   int i;
15845
15846   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15847     FreeGadget(game_gadget[i]);
15848 }
15849
15850 static void UnmapGameButtonsAtSamePosition(int id)
15851 {
15852   int i;
15853
15854   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15855     if (i != id &&
15856         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15857         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15858       UnmapGadget(game_gadget[i]);
15859 }
15860
15861 static void UnmapGameButtonsAtSamePosition_All(void)
15862 {
15863   if (setup.show_snapshot_buttons)
15864   {
15865     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15866     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15867     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15868   }
15869   else
15870   {
15871     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15872     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15873     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15874
15875     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15876     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15877     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15878   }
15879 }
15880
15881 static void MapGameButtonsAtSamePosition(int id)
15882 {
15883   int i;
15884
15885   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15886     if (i != id &&
15887         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15888         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15889       MapGadget(game_gadget[i]);
15890
15891   UnmapGameButtonsAtSamePosition_All();
15892 }
15893
15894 void MapUndoRedoButtons(void)
15895 {
15896   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15897   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15898
15899   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15900   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15901 }
15902
15903 void UnmapUndoRedoButtons(void)
15904 {
15905   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15906   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15907
15908   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15909   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15910 }
15911
15912 void ModifyPauseButtons(void)
15913 {
15914   static int ids[] =
15915   {
15916     GAME_CTRL_ID_PAUSE,
15917     GAME_CTRL_ID_PAUSE2,
15918     GAME_CTRL_ID_PANEL_PAUSE,
15919     GAME_CTRL_ID_TOUCH_PAUSE,
15920     -1
15921   };
15922   int i;
15923
15924   for (i = 0; ids[i] > -1; i++)
15925     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
15926 }
15927
15928 static void MapGameButtonsExt(boolean on_tape)
15929 {
15930   int i;
15931
15932   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15933     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15934         i != GAME_CTRL_ID_UNDO &&
15935         i != GAME_CTRL_ID_REDO)
15936       MapGadget(game_gadget[i]);
15937
15938   UnmapGameButtonsAtSamePosition_All();
15939
15940   RedrawGameButtons();
15941 }
15942
15943 static void UnmapGameButtonsExt(boolean on_tape)
15944 {
15945   int i;
15946
15947   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15948     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15949       UnmapGadget(game_gadget[i]);
15950 }
15951
15952 static void RedrawGameButtonsExt(boolean on_tape)
15953 {
15954   int i;
15955
15956   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15957     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15958       RedrawGadget(game_gadget[i]);
15959 }
15960
15961 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
15962 {
15963   if (gi == NULL)
15964     return;
15965
15966   gi->checked = state;
15967 }
15968
15969 static void RedrawSoundButtonGadget(int id)
15970 {
15971   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
15972              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
15973              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
15974              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
15975              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
15976              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15977              id);
15978
15979   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15980   RedrawGadget(game_gadget[id2]);
15981 }
15982
15983 void MapGameButtons(void)
15984 {
15985   MapGameButtonsExt(FALSE);
15986 }
15987
15988 void UnmapGameButtons(void)
15989 {
15990   UnmapGameButtonsExt(FALSE);
15991 }
15992
15993 void RedrawGameButtons(void)
15994 {
15995   RedrawGameButtonsExt(FALSE);
15996 }
15997
15998 void MapGameButtonsOnTape(void)
15999 {
16000   MapGameButtonsExt(TRUE);
16001 }
16002
16003 void UnmapGameButtonsOnTape(void)
16004 {
16005   UnmapGameButtonsExt(TRUE);
16006 }
16007
16008 void RedrawGameButtonsOnTape(void)
16009 {
16010   RedrawGameButtonsExt(TRUE);
16011 }
16012
16013 static void GameUndoRedoExt(void)
16014 {
16015   ClearPlayerAction();
16016
16017   tape.pausing = TRUE;
16018
16019   RedrawPlayfield();
16020   UpdateAndDisplayGameControlValues();
16021
16022   DrawCompleteVideoDisplay();
16023   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16024   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16025   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16026
16027   BackToFront();
16028 }
16029
16030 static void GameUndo(int steps)
16031 {
16032   if (!CheckEngineSnapshotList())
16033     return;
16034
16035   LoadEngineSnapshot_Undo(steps);
16036
16037   GameUndoRedoExt();
16038 }
16039
16040 static void GameRedo(int steps)
16041 {
16042   if (!CheckEngineSnapshotList())
16043     return;
16044
16045   LoadEngineSnapshot_Redo(steps);
16046
16047   GameUndoRedoExt();
16048 }
16049
16050 static void HandleGameButtonsExt(int id, int button)
16051 {
16052   static boolean game_undo_executed = FALSE;
16053   int steps = BUTTON_STEPSIZE(button);
16054   boolean handle_game_buttons =
16055     (game_status == GAME_MODE_PLAYING ||
16056      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16057
16058   if (!handle_game_buttons)
16059     return;
16060
16061   switch (id)
16062   {
16063     case GAME_CTRL_ID_STOP:
16064     case GAME_CTRL_ID_PANEL_STOP:
16065     case GAME_CTRL_ID_TOUCH_STOP:
16066       if (game_status == GAME_MODE_MAIN)
16067         break;
16068
16069       if (tape.playing)
16070         TapeStop();
16071       else
16072         RequestQuitGame(TRUE);
16073
16074       break;
16075
16076     case GAME_CTRL_ID_PAUSE:
16077     case GAME_CTRL_ID_PAUSE2:
16078     case GAME_CTRL_ID_PANEL_PAUSE:
16079     case GAME_CTRL_ID_TOUCH_PAUSE:
16080       if (network.enabled && game_status == GAME_MODE_PLAYING)
16081       {
16082         if (tape.pausing)
16083           SendToServer_ContinuePlaying();
16084         else
16085           SendToServer_PausePlaying();
16086       }
16087       else
16088         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16089
16090       game_undo_executed = FALSE;
16091
16092       break;
16093
16094     case GAME_CTRL_ID_PLAY:
16095     case GAME_CTRL_ID_PANEL_PLAY:
16096       if (game_status == GAME_MODE_MAIN)
16097       {
16098         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16099       }
16100       else if (tape.pausing)
16101       {
16102         if (network.enabled)
16103           SendToServer_ContinuePlaying();
16104         else
16105           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16106       }
16107       break;
16108
16109     case GAME_CTRL_ID_UNDO:
16110       // Important: When using "save snapshot when collecting an item" mode,
16111       // load last (current) snapshot for first "undo" after pressing "pause"
16112       // (else the last-but-one snapshot would be loaded, because the snapshot
16113       // pointer already points to the last snapshot when pressing "pause",
16114       // which is fine for "every step/move" mode, but not for "every collect")
16115       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16116           !game_undo_executed)
16117         steps--;
16118
16119       game_undo_executed = TRUE;
16120
16121       GameUndo(steps);
16122       break;
16123
16124     case GAME_CTRL_ID_REDO:
16125       GameRedo(steps);
16126       break;
16127
16128     case GAME_CTRL_ID_SAVE:
16129       TapeQuickSave();
16130       break;
16131
16132     case GAME_CTRL_ID_LOAD:
16133       TapeQuickLoad();
16134       break;
16135
16136     case SOUND_CTRL_ID_MUSIC:
16137     case SOUND_CTRL_ID_PANEL_MUSIC:
16138       if (setup.sound_music)
16139       { 
16140         setup.sound_music = FALSE;
16141
16142         FadeMusic();
16143       }
16144       else if (audio.music_available)
16145       { 
16146         setup.sound = setup.sound_music = TRUE;
16147
16148         SetAudioMode(setup.sound);
16149
16150         if (game_status == GAME_MODE_PLAYING)
16151           PlayLevelMusic();
16152       }
16153
16154       RedrawSoundButtonGadget(id);
16155
16156       break;
16157
16158     case SOUND_CTRL_ID_LOOPS:
16159     case SOUND_CTRL_ID_PANEL_LOOPS:
16160       if (setup.sound_loops)
16161         setup.sound_loops = FALSE;
16162       else if (audio.loops_available)
16163       {
16164         setup.sound = setup.sound_loops = TRUE;
16165
16166         SetAudioMode(setup.sound);
16167       }
16168
16169       RedrawSoundButtonGadget(id);
16170
16171       break;
16172
16173     case SOUND_CTRL_ID_SIMPLE:
16174     case SOUND_CTRL_ID_PANEL_SIMPLE:
16175       if (setup.sound_simple)
16176         setup.sound_simple = FALSE;
16177       else if (audio.sound_available)
16178       {
16179         setup.sound = setup.sound_simple = TRUE;
16180
16181         SetAudioMode(setup.sound);
16182       }
16183
16184       RedrawSoundButtonGadget(id);
16185
16186       break;
16187
16188     default:
16189       break;
16190   }
16191 }
16192
16193 static void HandleGameButtons(struct GadgetInfo *gi)
16194 {
16195   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16196 }
16197
16198 void HandleSoundButtonKeys(Key key)
16199 {
16200   if (key == setup.shortcut.sound_simple)
16201     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16202   else if (key == setup.shortcut.sound_loops)
16203     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16204   else if (key == setup.shortcut.sound_music)
16205     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16206 }