5968b1ded7e0abc96e391632bb64cae47c948050
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 // DEBUG SETTINGS
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 // EXPERIMENTAL STUFF
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 // EXPERIMENTAL STUFF
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 // for DigField()
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 // for MovePlayer()
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 // for ScrollPlayer()
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 // for Bang()/Explode()
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 // game panel display and control definitions
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 // values for delayed check of falling and moving elements and for collision
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 // values for initial player move delay (initial delay counter value)
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 // values for player movement speed (which is in fact a delay value)
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 // values for scroll positions
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 // values for other actions
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
883                                  RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
886                                  RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
888                                  RND((c)->delay_random))
889
890
891 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
892          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
893
894 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
895         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
896          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
897          (be) + (e) - EL_SELF)
898
899 #define GET_PLAYER_FROM_BITS(p)                                         \
900         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
901
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
903         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
904          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
905          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
906          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
907          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
908          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
909          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
910          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
911          (e))
912
913 #define CAN_GROW_INTO(e)                                                \
914         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
915
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
917                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
918                                         (condition)))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (CAN_MOVE_INTO_ACID(e) &&       \
923                                          Tile[x][y] == EL_ACID) ||      \
924                                         (condition)))
925
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
927                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
928                                         (CAN_MOVE_INTO_ACID(e) &&       \
929                                          Tile[x][y] == EL_ACID) ||      \
930                                         (condition)))
931
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
933                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
934                                         (condition) ||                  \
935                                         (CAN_MOVE_INTO_ACID(e) &&       \
936                                          Tile[x][y] == EL_ACID) ||      \
937                                         (DONT_COLLIDE_WITH(e) &&        \
938                                          IS_PLAYER(x, y) &&             \
939                                          !PLAYER_ENEMY_PROTECTED(x, y))))
940
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
942         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
943
944 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
945         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
946
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
948         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Tile[x][y] == EL_EMC_PLANT)
949
950 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
951         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Tile[x][y]) || \
952                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
953
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
955         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
956
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
958         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Tile[x][y] == EL_DIAMOND)
959
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
961         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Tile[x][y]))
962
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
964         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Tile[x][y]))
965
966 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
967         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Tile[x][y]))
968
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
970         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Tile[x][y] == EL_EXIT_OPEN || \
971                                                  Tile[x][y] == EL_EM_EXIT_OPEN || \
972                                                  Tile[x][y] == EL_STEEL_EXIT_OPEN || \
973                                                  Tile[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974                                                  IS_FOOD_PENGUIN(Tile[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
976         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
977
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
979         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
980
981 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
982         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
983
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
985         (IN_LEV_FIELD(x, y) && (Tile[x][y] == EL_EMC_SPRING_BUMPER ||   \
986                                 Tile[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
987
988 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
989
990 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
991                 (!IS_PLAYER(x, y) &&                                    \
992                  IS_EQUAL_OR_IN_GROUP(Tile[x][y], MOVE_ENTER_EL(e)))
993
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
995         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
996
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
999
1000 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1004
1005 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1006
1007 // game button identifiers
1008 #define GAME_CTRL_ID_STOP               0
1009 #define GAME_CTRL_ID_PAUSE              1
1010 #define GAME_CTRL_ID_PLAY               2
1011 #define GAME_CTRL_ID_UNDO               3
1012 #define GAME_CTRL_ID_REDO               4
1013 #define GAME_CTRL_ID_SAVE               5
1014 #define GAME_CTRL_ID_PAUSE2             6
1015 #define GAME_CTRL_ID_LOAD               7
1016 #define GAME_CTRL_ID_PANEL_STOP         8
1017 #define GAME_CTRL_ID_PANEL_PAUSE        9
1018 #define GAME_CTRL_ID_PANEL_PLAY         10
1019 #define GAME_CTRL_ID_TOUCH_STOP         11
1020 #define GAME_CTRL_ID_TOUCH_PAUSE        12
1021 #define SOUND_CTRL_ID_MUSIC             13
1022 #define SOUND_CTRL_ID_LOOPS             14
1023 #define SOUND_CTRL_ID_SIMPLE            15
1024 #define SOUND_CTRL_ID_PANEL_MUSIC       16
1025 #define SOUND_CTRL_ID_PANEL_LOOPS       17
1026 #define SOUND_CTRL_ID_PANEL_SIMPLE      18
1027
1028 #define NUM_GAME_BUTTONS                19
1029
1030
1031 // forward declaration for internal use
1032
1033 static void CreateField(int, int, int);
1034
1035 static void ResetGfxAnimation(int, int);
1036
1037 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1038 static void AdvanceFrameAndPlayerCounters(int);
1039
1040 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1041 static boolean MovePlayer(struct PlayerInfo *, int, int);
1042 static void ScrollPlayer(struct PlayerInfo *, int);
1043 static void ScrollScreen(struct PlayerInfo *, int);
1044
1045 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1046 static boolean DigFieldByCE(int, int, int);
1047 static boolean SnapField(struct PlayerInfo *, int, int);
1048 static boolean DropElement(struct PlayerInfo *);
1049
1050 static void InitBeltMovement(void);
1051 static void CloseAllOpenTimegates(void);
1052 static void CheckGravityMovement(struct PlayerInfo *);
1053 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1054 static void KillPlayerUnlessEnemyProtected(int, int);
1055 static void KillPlayerUnlessExplosionProtected(int, int);
1056
1057 static void TestIfPlayerTouchesCustomElement(int, int);
1058 static void TestIfElementTouchesCustomElement(int, int);
1059 static void TestIfElementHitsCustomElement(int, int, int);
1060
1061 static void HandleElementChange(int, int, int);
1062 static void ExecuteCustomElementAction(int, int, int, int);
1063 static boolean ChangeElement(int, int, int, int);
1064
1065 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1066 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1067         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1068 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1069         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1070 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1071         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1072 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1073         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1074
1075 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1076 #define CheckElementChange(x, y, e, te, ev)                             \
1077         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1078 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1079         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1080 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1081         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1082
1083 static void PlayLevelSound(int, int, int);
1084 static void PlayLevelSoundNearest(int, int, int);
1085 static void PlayLevelSoundAction(int, int, int);
1086 static void PlayLevelSoundElementAction(int, int, int, int);
1087 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1088 static void PlayLevelSoundActionIfLoop(int, int, int);
1089 static void StopLevelSoundActionIfLoop(int, int, int);
1090 static void PlayLevelMusic(void);
1091 static void FadeLevelSoundsAndMusic(void);
1092
1093 static void HandleGameButtons(struct GadgetInfo *);
1094
1095 int AmoebaNeighbourNr(int, int);
1096 void AmoebaToDiamond(int, int);
1097 void ContinueMoving(int, int);
1098 void Bang(int, int);
1099 void InitMovDir(int, int);
1100 void InitAmoebaNr(int, int);
1101 int NewHiScore(int);
1102
1103 void TestIfGoodThingHitsBadThing(int, int, int);
1104 void TestIfBadThingHitsGoodThing(int, int, int);
1105 void TestIfPlayerTouchesBadThing(int, int);
1106 void TestIfPlayerRunsIntoBadThing(int, int, int);
1107 void TestIfBadThingTouchesPlayer(int, int);
1108 void TestIfBadThingRunsIntoPlayer(int, int, int);
1109 void TestIfFriendTouchesBadThing(int, int);
1110 void TestIfBadThingTouchesFriend(int, int);
1111 void TestIfBadThingTouchesOtherBadThing(int, int);
1112 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1113
1114 void KillPlayer(struct PlayerInfo *);
1115 void BuryPlayer(struct PlayerInfo *);
1116 void RemovePlayer(struct PlayerInfo *);
1117 void ExitPlayer(struct PlayerInfo *);
1118
1119 static int getInvisibleActiveFromInvisibleElement(int);
1120 static int getInvisibleFromInvisibleActiveElement(int);
1121
1122 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1123
1124 // for detection of endless loops, caused by custom element programming
1125 // (using maximal playfield width x 10 is just a rough approximation)
1126 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1127
1128 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1129 {                                                                       \
1130   if (recursion_loop_detected)                                          \
1131     return (rc);                                                        \
1132                                                                         \
1133   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1134   {                                                                     \
1135     recursion_loop_detected = TRUE;                                     \
1136     recursion_loop_element = (e);                                       \
1137   }                                                                     \
1138                                                                         \
1139   recursion_loop_depth++;                                               \
1140 }
1141
1142 #define RECURSION_LOOP_DETECTION_END()                                  \
1143 {                                                                       \
1144   recursion_loop_depth--;                                               \
1145 }
1146
1147 static int recursion_loop_depth;
1148 static boolean recursion_loop_detected;
1149 static boolean recursion_loop_element;
1150
1151 static int map_player_action[MAX_PLAYERS];
1152
1153
1154 // ----------------------------------------------------------------------------
1155 // definition of elements that automatically change to other elements after
1156 // a specified time, eventually calling a function when changing
1157 // ----------------------------------------------------------------------------
1158
1159 // forward declaration for changer functions
1160 static void InitBuggyBase(int, int);
1161 static void WarnBuggyBase(int, int);
1162
1163 static void InitTrap(int, int);
1164 static void ActivateTrap(int, int);
1165 static void ChangeActiveTrap(int, int);
1166
1167 static void InitRobotWheel(int, int);
1168 static void RunRobotWheel(int, int);
1169 static void StopRobotWheel(int, int);
1170
1171 static void InitTimegateWheel(int, int);
1172 static void RunTimegateWheel(int, int);
1173
1174 static void InitMagicBallDelay(int, int);
1175 static void ActivateMagicBall(int, int);
1176
1177 struct ChangingElementInfo
1178 {
1179   int element;
1180   int target_element;
1181   int change_delay;
1182   void (*pre_change_function)(int x, int y);
1183   void (*change_function)(int x, int y);
1184   void (*post_change_function)(int x, int y);
1185 };
1186
1187 static struct ChangingElementInfo change_delay_list[] =
1188 {
1189   {
1190     EL_NUT_BREAKING,
1191     EL_EMERALD,
1192     6,
1193     NULL,
1194     NULL,
1195     NULL
1196   },
1197   {
1198     EL_PEARL_BREAKING,
1199     EL_EMPTY,
1200     8,
1201     NULL,
1202     NULL,
1203     NULL
1204   },
1205   {
1206     EL_EXIT_OPENING,
1207     EL_EXIT_OPEN,
1208     29,
1209     NULL,
1210     NULL,
1211     NULL
1212   },
1213   {
1214     EL_EXIT_CLOSING,
1215     EL_EXIT_CLOSED,
1216     29,
1217     NULL,
1218     NULL,
1219     NULL
1220   },
1221   {
1222     EL_STEEL_EXIT_OPENING,
1223     EL_STEEL_EXIT_OPEN,
1224     29,
1225     NULL,
1226     NULL,
1227     NULL
1228   },
1229   {
1230     EL_STEEL_EXIT_CLOSING,
1231     EL_STEEL_EXIT_CLOSED,
1232     29,
1233     NULL,
1234     NULL,
1235     NULL
1236   },
1237   {
1238     EL_EM_EXIT_OPENING,
1239     EL_EM_EXIT_OPEN,
1240     29,
1241     NULL,
1242     NULL,
1243     NULL
1244   },
1245   {
1246     EL_EM_EXIT_CLOSING,
1247     EL_EMPTY,
1248     29,
1249     NULL,
1250     NULL,
1251     NULL
1252   },
1253   {
1254     EL_EM_STEEL_EXIT_OPENING,
1255     EL_EM_STEEL_EXIT_OPEN,
1256     29,
1257     NULL,
1258     NULL,
1259     NULL
1260   },
1261   {
1262     EL_EM_STEEL_EXIT_CLOSING,
1263     EL_STEELWALL,
1264     29,
1265     NULL,
1266     NULL,
1267     NULL
1268   },
1269   {
1270     EL_SP_EXIT_OPENING,
1271     EL_SP_EXIT_OPEN,
1272     29,
1273     NULL,
1274     NULL,
1275     NULL
1276   },
1277   {
1278     EL_SP_EXIT_CLOSING,
1279     EL_SP_EXIT_CLOSED,
1280     29,
1281     NULL,
1282     NULL,
1283     NULL
1284   },
1285   {
1286     EL_SWITCHGATE_OPENING,
1287     EL_SWITCHGATE_OPEN,
1288     29,
1289     NULL,
1290     NULL,
1291     NULL
1292   },
1293   {
1294     EL_SWITCHGATE_CLOSING,
1295     EL_SWITCHGATE_CLOSED,
1296     29,
1297     NULL,
1298     NULL,
1299     NULL
1300   },
1301   {
1302     EL_TIMEGATE_OPENING,
1303     EL_TIMEGATE_OPEN,
1304     29,
1305     NULL,
1306     NULL,
1307     NULL
1308   },
1309   {
1310     EL_TIMEGATE_CLOSING,
1311     EL_TIMEGATE_CLOSED,
1312     29,
1313     NULL,
1314     NULL,
1315     NULL
1316   },
1317
1318   {
1319     EL_ACID_SPLASH_LEFT,
1320     EL_EMPTY,
1321     8,
1322     NULL,
1323     NULL,
1324     NULL
1325   },
1326   {
1327     EL_ACID_SPLASH_RIGHT,
1328     EL_EMPTY,
1329     8,
1330     NULL,
1331     NULL,
1332     NULL
1333   },
1334   {
1335     EL_SP_BUGGY_BASE,
1336     EL_SP_BUGGY_BASE_ACTIVATING,
1337     0,
1338     InitBuggyBase,
1339     NULL,
1340     NULL
1341   },
1342   {
1343     EL_SP_BUGGY_BASE_ACTIVATING,
1344     EL_SP_BUGGY_BASE_ACTIVE,
1345     0,
1346     InitBuggyBase,
1347     NULL,
1348     NULL
1349   },
1350   {
1351     EL_SP_BUGGY_BASE_ACTIVE,
1352     EL_SP_BUGGY_BASE,
1353     0,
1354     InitBuggyBase,
1355     WarnBuggyBase,
1356     NULL
1357   },
1358   {
1359     EL_TRAP,
1360     EL_TRAP_ACTIVE,
1361     0,
1362     InitTrap,
1363     NULL,
1364     ActivateTrap
1365   },
1366   {
1367     EL_TRAP_ACTIVE,
1368     EL_TRAP,
1369     31,
1370     NULL,
1371     ChangeActiveTrap,
1372     NULL
1373   },
1374   {
1375     EL_ROBOT_WHEEL_ACTIVE,
1376     EL_ROBOT_WHEEL,
1377     0,
1378     InitRobotWheel,
1379     RunRobotWheel,
1380     StopRobotWheel
1381   },
1382   {
1383     EL_TIMEGATE_SWITCH_ACTIVE,
1384     EL_TIMEGATE_SWITCH,
1385     0,
1386     InitTimegateWheel,
1387     RunTimegateWheel,
1388     NULL
1389   },
1390   {
1391     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1392     EL_DC_TIMEGATE_SWITCH,
1393     0,
1394     InitTimegateWheel,
1395     RunTimegateWheel,
1396     NULL
1397   },
1398   {
1399     EL_EMC_MAGIC_BALL_ACTIVE,
1400     EL_EMC_MAGIC_BALL_ACTIVE,
1401     0,
1402     InitMagicBallDelay,
1403     NULL,
1404     ActivateMagicBall
1405   },
1406   {
1407     EL_EMC_SPRING_BUMPER_ACTIVE,
1408     EL_EMC_SPRING_BUMPER,
1409     8,
1410     NULL,
1411     NULL,
1412     NULL
1413   },
1414   {
1415     EL_DIAGONAL_SHRINKING,
1416     EL_UNDEFINED,
1417     0,
1418     NULL,
1419     NULL,
1420     NULL
1421   },
1422   {
1423     EL_DIAGONAL_GROWING,
1424     EL_UNDEFINED,
1425     0,
1426     NULL,
1427     NULL,
1428     NULL,
1429   },
1430
1431   {
1432     EL_UNDEFINED,
1433     EL_UNDEFINED,
1434     -1,
1435     NULL,
1436     NULL,
1437     NULL
1438   }
1439 };
1440
1441 struct
1442 {
1443   int element;
1444   int push_delay_fixed, push_delay_random;
1445 }
1446 push_delay_list[] =
1447 {
1448   { EL_SPRING,                  0, 0 },
1449   { EL_BALLOON,                 0, 0 },
1450
1451   { EL_SOKOBAN_OBJECT,          2, 0 },
1452   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1453   { EL_SATELLITE,               2, 0 },
1454   { EL_SP_DISK_YELLOW,          2, 0 },
1455
1456   { EL_UNDEFINED,               0, 0 },
1457 };
1458
1459 struct
1460 {
1461   int element;
1462   int move_stepsize;
1463 }
1464 move_stepsize_list[] =
1465 {
1466   { EL_AMOEBA_DROP,             2 },
1467   { EL_AMOEBA_DROPPING,         2 },
1468   { EL_QUICKSAND_FILLING,       1 },
1469   { EL_QUICKSAND_EMPTYING,      1 },
1470   { EL_QUICKSAND_FAST_FILLING,  2 },
1471   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1472   { EL_MAGIC_WALL_FILLING,      2 },
1473   { EL_MAGIC_WALL_EMPTYING,     2 },
1474   { EL_BD_MAGIC_WALL_FILLING,   2 },
1475   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1476   { EL_DC_MAGIC_WALL_FILLING,   2 },
1477   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1478
1479   { EL_UNDEFINED,               0 },
1480 };
1481
1482 struct
1483 {
1484   int element;
1485   int count;
1486 }
1487 collect_count_list[] =
1488 {
1489   { EL_EMERALD,                 1 },
1490   { EL_BD_DIAMOND,              1 },
1491   { EL_EMERALD_YELLOW,          1 },
1492   { EL_EMERALD_RED,             1 },
1493   { EL_EMERALD_PURPLE,          1 },
1494   { EL_DIAMOND,                 3 },
1495   { EL_SP_INFOTRON,             1 },
1496   { EL_PEARL,                   5 },
1497   { EL_CRYSTAL,                 8 },
1498
1499   { EL_UNDEFINED,               0 },
1500 };
1501
1502 struct
1503 {
1504   int element;
1505   int direction;
1506 }
1507 access_direction_list[] =
1508 {
1509   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1510   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1511   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1512   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1513   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1514   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1515   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1516   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1517   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1518   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1519   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1520
1521   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1522   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1523   { EL_SP_PORT_UP,                                                   MV_DOWN },
1524   { EL_SP_PORT_DOWN,                                         MV_UP           },
1525   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1526   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1527   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1528   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1529   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1530   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1531   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1532   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1533   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1534   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1535   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1536   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1537   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1538   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1539   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1540
1541   { EL_UNDEFINED,                       MV_NONE                              }
1542 };
1543
1544 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1545
1546 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1547 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1548 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1549                                  IS_JUST_CHANGING(x, y))
1550
1551 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1552
1553 // static variables for playfield scan mode (scanning forward or backward)
1554 static int playfield_scan_start_x = 0;
1555 static int playfield_scan_start_y = 0;
1556 static int playfield_scan_delta_x = 1;
1557 static int playfield_scan_delta_y = 1;
1558
1559 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1560                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1561                                      (y) += playfield_scan_delta_y)     \
1562                                 for ((x) = playfield_scan_start_x;      \
1563                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1564                                      (x) += playfield_scan_delta_x)
1565
1566 #ifdef DEBUG
1567 void DEBUG_SetMaximumDynamite(void)
1568 {
1569   int i;
1570
1571   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1572     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1573       local_player->inventory_element[local_player->inventory_size++] =
1574         EL_DYNAMITE;
1575 }
1576 #endif
1577
1578 static void InitPlayfieldScanModeVars(void)
1579 {
1580   if (game.use_reverse_scan_direction)
1581   {
1582     playfield_scan_start_x = lev_fieldx - 1;
1583     playfield_scan_start_y = lev_fieldy - 1;
1584
1585     playfield_scan_delta_x = -1;
1586     playfield_scan_delta_y = -1;
1587   }
1588   else
1589   {
1590     playfield_scan_start_x = 0;
1591     playfield_scan_start_y = 0;
1592
1593     playfield_scan_delta_x = 1;
1594     playfield_scan_delta_y = 1;
1595   }
1596 }
1597
1598 static void InitPlayfieldScanMode(int mode)
1599 {
1600   game.use_reverse_scan_direction =
1601     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1602
1603   InitPlayfieldScanModeVars();
1604 }
1605
1606 static int get_move_delay_from_stepsize(int move_stepsize)
1607 {
1608   move_stepsize =
1609     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1610
1611   // make sure that stepsize value is always a power of 2
1612   move_stepsize = (1 << log_2(move_stepsize));
1613
1614   return TILEX / move_stepsize;
1615 }
1616
1617 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1618                                boolean init_game)
1619 {
1620   int player_nr = player->index_nr;
1621   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1622   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1623
1624   // do no immediately change move delay -- the player might just be moving
1625   player->move_delay_value_next = move_delay;
1626
1627   // information if player can move must be set separately
1628   player->cannot_move = cannot_move;
1629
1630   if (init_game)
1631   {
1632     player->move_delay       = game.initial_move_delay[player_nr];
1633     player->move_delay_value = game.initial_move_delay_value[player_nr];
1634
1635     player->move_delay_value_next = -1;
1636
1637     player->move_delay_reset_counter = 0;
1638   }
1639 }
1640
1641 void GetPlayerConfig(void)
1642 {
1643   GameFrameDelay = setup.game_frame_delay;
1644
1645   if (!audio.sound_available)
1646     setup.sound_simple = FALSE;
1647
1648   if (!audio.loops_available)
1649     setup.sound_loops = FALSE;
1650
1651   if (!audio.music_available)
1652     setup.sound_music = FALSE;
1653
1654   if (!video.fullscreen_available)
1655     setup.fullscreen = FALSE;
1656
1657   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1658
1659   SetAudioMode(setup.sound);
1660 }
1661
1662 int GetElementFromGroupElement(int element)
1663 {
1664   if (IS_GROUP_ELEMENT(element))
1665   {
1666     struct ElementGroupInfo *group = element_info[element].group;
1667     int last_anim_random_frame = gfx.anim_random_frame;
1668     int element_pos;
1669
1670     if (group->choice_mode == ANIM_RANDOM)
1671       gfx.anim_random_frame = RND(group->num_elements_resolved);
1672
1673     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1674                                     group->choice_mode, 0,
1675                                     group->choice_pos);
1676
1677     if (group->choice_mode == ANIM_RANDOM)
1678       gfx.anim_random_frame = last_anim_random_frame;
1679
1680     group->choice_pos++;
1681
1682     element = group->element_resolved[element_pos];
1683   }
1684
1685   return element;
1686 }
1687
1688 static void IncrementSokobanFieldsNeeded(void)
1689 {
1690   if (level.sb_fields_needed)
1691     game.sokoban_fields_still_needed++;
1692 }
1693
1694 static void IncrementSokobanObjectsNeeded(void)
1695 {
1696   if (level.sb_objects_needed)
1697     game.sokoban_objects_still_needed++;
1698 }
1699
1700 static void DecrementSokobanFieldsNeeded(void)
1701 {
1702   if (game.sokoban_fields_still_needed > 0)
1703     game.sokoban_fields_still_needed--;
1704 }
1705
1706 static void DecrementSokobanObjectsNeeded(void)
1707 {
1708   if (game.sokoban_objects_still_needed > 0)
1709     game.sokoban_objects_still_needed--;
1710 }
1711
1712 static void InitPlayerField(int x, int y, int element, boolean init_game)
1713 {
1714   if (element == EL_SP_MURPHY)
1715   {
1716     if (init_game)
1717     {
1718       if (stored_player[0].present)
1719       {
1720         Tile[x][y] = EL_SP_MURPHY_CLONE;
1721
1722         return;
1723       }
1724       else
1725       {
1726         stored_player[0].initial_element = element;
1727         stored_player[0].use_murphy = TRUE;
1728
1729         if (!level.use_artwork_element[0])
1730           stored_player[0].artwork_element = EL_SP_MURPHY;
1731       }
1732
1733       Tile[x][y] = EL_PLAYER_1;
1734     }
1735   }
1736
1737   if (init_game)
1738   {
1739     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1740     int jx = player->jx, jy = player->jy;
1741
1742     player->present = TRUE;
1743
1744     player->block_last_field = (element == EL_SP_MURPHY ?
1745                                 level.sp_block_last_field :
1746                                 level.block_last_field);
1747
1748     // ---------- initialize player's last field block delay ------------------
1749
1750     // always start with reliable default value (no adjustment needed)
1751     player->block_delay_adjustment = 0;
1752
1753     // special case 1: in Supaplex, Murphy blocks last field one more frame
1754     if (player->block_last_field && element == EL_SP_MURPHY)
1755       player->block_delay_adjustment = 1;
1756
1757     // special case 2: in game engines before 3.1.1, blocking was different
1758     if (game.use_block_last_field_bug)
1759       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1760
1761     if (!network.enabled || player->connected_network)
1762     {
1763       player->active = TRUE;
1764
1765       // remove potentially duplicate players
1766       if (StorePlayer[jx][jy] == Tile[x][y])
1767         StorePlayer[jx][jy] = 0;
1768
1769       StorePlayer[x][y] = Tile[x][y];
1770
1771 #if DEBUG_INIT_PLAYER
1772       Debug("game:init:player", "- player element %d activated",
1773             player->element_nr);
1774       Debug("game:init:player", "  (local player is %d and currently %s)",
1775             local_player->element_nr,
1776             local_player->active ? "active" : "not active");
1777     }
1778 #endif
1779
1780     Tile[x][y] = EL_EMPTY;
1781
1782     player->jx = player->last_jx = x;
1783     player->jy = player->last_jy = y;
1784   }
1785
1786   // always check if player was just killed and should be reanimated
1787   {
1788     int player_nr = GET_PLAYER_NR(element);
1789     struct PlayerInfo *player = &stored_player[player_nr];
1790
1791     if (player->active && player->killed)
1792       player->reanimated = TRUE; // if player was just killed, reanimate him
1793   }
1794 }
1795
1796 static void InitField(int x, int y, boolean init_game)
1797 {
1798   int element = Tile[x][y];
1799
1800   switch (element)
1801   {
1802     case EL_SP_MURPHY:
1803     case EL_PLAYER_1:
1804     case EL_PLAYER_2:
1805     case EL_PLAYER_3:
1806     case EL_PLAYER_4:
1807       InitPlayerField(x, y, element, init_game);
1808       break;
1809
1810     case EL_SOKOBAN_FIELD_PLAYER:
1811       element = Tile[x][y] = EL_PLAYER_1;
1812       InitField(x, y, init_game);
1813
1814       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1815       InitField(x, y, init_game);
1816       break;
1817
1818     case EL_SOKOBAN_FIELD_EMPTY:
1819       IncrementSokobanFieldsNeeded();
1820       break;
1821
1822     case EL_SOKOBAN_OBJECT:
1823       IncrementSokobanObjectsNeeded();
1824       break;
1825
1826     case EL_STONEBLOCK:
1827       if (x < lev_fieldx-1 && Tile[x+1][y] == EL_ACID)
1828         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1829       else if (x > 0 && Tile[x-1][y] == EL_ACID)
1830         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1831       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPLEFT)
1832         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1833       else if (y > 0 && Tile[x][y-1] == EL_ACID)
1834         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1835       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1836         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1837       break;
1838
1839     case EL_BUG:
1840     case EL_BUG_RIGHT:
1841     case EL_BUG_UP:
1842     case EL_BUG_LEFT:
1843     case EL_BUG_DOWN:
1844     case EL_SPACESHIP:
1845     case EL_SPACESHIP_RIGHT:
1846     case EL_SPACESHIP_UP:
1847     case EL_SPACESHIP_LEFT:
1848     case EL_SPACESHIP_DOWN:
1849     case EL_BD_BUTTERFLY:
1850     case EL_BD_BUTTERFLY_RIGHT:
1851     case EL_BD_BUTTERFLY_UP:
1852     case EL_BD_BUTTERFLY_LEFT:
1853     case EL_BD_BUTTERFLY_DOWN:
1854     case EL_BD_FIREFLY:
1855     case EL_BD_FIREFLY_RIGHT:
1856     case EL_BD_FIREFLY_UP:
1857     case EL_BD_FIREFLY_LEFT:
1858     case EL_BD_FIREFLY_DOWN:
1859     case EL_PACMAN_RIGHT:
1860     case EL_PACMAN_UP:
1861     case EL_PACMAN_LEFT:
1862     case EL_PACMAN_DOWN:
1863     case EL_YAMYAM:
1864     case EL_YAMYAM_LEFT:
1865     case EL_YAMYAM_RIGHT:
1866     case EL_YAMYAM_UP:
1867     case EL_YAMYAM_DOWN:
1868     case EL_DARK_YAMYAM:
1869     case EL_ROBOT:
1870     case EL_PACMAN:
1871     case EL_SP_SNIKSNAK:
1872     case EL_SP_ELECTRON:
1873     case EL_MOLE:
1874     case EL_MOLE_LEFT:
1875     case EL_MOLE_RIGHT:
1876     case EL_MOLE_UP:
1877     case EL_MOLE_DOWN:
1878     case EL_SPRING_LEFT:
1879     case EL_SPRING_RIGHT:
1880       InitMovDir(x, y);
1881       break;
1882
1883     case EL_AMOEBA_FULL:
1884     case EL_BD_AMOEBA:
1885       InitAmoebaNr(x, y);
1886       break;
1887
1888     case EL_AMOEBA_DROP:
1889       if (y == lev_fieldy - 1)
1890       {
1891         Tile[x][y] = EL_AMOEBA_GROWING;
1892         Store[x][y] = EL_AMOEBA_WET;
1893       }
1894       break;
1895
1896     case EL_DYNAMITE_ACTIVE:
1897     case EL_SP_DISK_RED_ACTIVE:
1898     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1899     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1900     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1901     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1902       MovDelay[x][y] = 96;
1903       break;
1904
1905     case EL_EM_DYNAMITE_ACTIVE:
1906       MovDelay[x][y] = 32;
1907       break;
1908
1909     case EL_LAMP:
1910       game.lights_still_needed++;
1911       break;
1912
1913     case EL_PENGUIN:
1914       game.friends_still_needed++;
1915       break;
1916
1917     case EL_PIG:
1918     case EL_DRAGON:
1919       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1920       break;
1921
1922     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1923     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1924     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1925     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1926     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1927     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1928     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1929     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1930     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1931     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1932     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1933     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1934       if (init_game)
1935       {
1936         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1937         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1938         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1939
1940         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1941         {
1942           game.belt_dir[belt_nr] = belt_dir;
1943           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1944         }
1945         else    // more than one switch -- set it like the first switch
1946         {
1947           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1948         }
1949       }
1950       break;
1951
1952     case EL_LIGHT_SWITCH_ACTIVE:
1953       if (init_game)
1954         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1955       break;
1956
1957     case EL_INVISIBLE_STEELWALL:
1958     case EL_INVISIBLE_WALL:
1959     case EL_INVISIBLE_SAND:
1960       if (game.light_time_left > 0 ||
1961           game.lenses_time_left > 0)
1962         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1963       break;
1964
1965     case EL_EMC_MAGIC_BALL:
1966       if (game.ball_active)
1967         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1968       break;
1969
1970     case EL_EMC_MAGIC_BALL_SWITCH:
1971       if (game.ball_active)
1972         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1973       break;
1974
1975     case EL_TRIGGER_PLAYER:
1976     case EL_TRIGGER_ELEMENT:
1977     case EL_TRIGGER_CE_VALUE:
1978     case EL_TRIGGER_CE_SCORE:
1979     case EL_SELF:
1980     case EL_ANY_ELEMENT:
1981     case EL_CURRENT_CE_VALUE:
1982     case EL_CURRENT_CE_SCORE:
1983     case EL_PREV_CE_1:
1984     case EL_PREV_CE_2:
1985     case EL_PREV_CE_3:
1986     case EL_PREV_CE_4:
1987     case EL_PREV_CE_5:
1988     case EL_PREV_CE_6:
1989     case EL_PREV_CE_7:
1990     case EL_PREV_CE_8:
1991     case EL_NEXT_CE_1:
1992     case EL_NEXT_CE_2:
1993     case EL_NEXT_CE_3:
1994     case EL_NEXT_CE_4:
1995     case EL_NEXT_CE_5:
1996     case EL_NEXT_CE_6:
1997     case EL_NEXT_CE_7:
1998     case EL_NEXT_CE_8:
1999       // reference elements should not be used on the playfield
2000       Tile[x][y] = EL_EMPTY;
2001       break;
2002
2003     default:
2004       if (IS_CUSTOM_ELEMENT(element))
2005       {
2006         if (CAN_MOVE(element))
2007           InitMovDir(x, y);
2008
2009         if (!element_info[element].use_last_ce_value || init_game)
2010           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2011       }
2012       else if (IS_GROUP_ELEMENT(element))
2013       {
2014         Tile[x][y] = GetElementFromGroupElement(element);
2015
2016         InitField(x, y, init_game);
2017       }
2018
2019       break;
2020   }
2021
2022   if (!init_game)
2023     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2024 }
2025
2026 static void InitField_WithBug1(int x, int y, boolean init_game)
2027 {
2028   InitField(x, y, init_game);
2029
2030   // not needed to call InitMovDir() -- already done by InitField()!
2031   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2032       CAN_MOVE(Tile[x][y]))
2033     InitMovDir(x, y);
2034 }
2035
2036 static void InitField_WithBug2(int x, int y, boolean init_game)
2037 {
2038   int old_element = Tile[x][y];
2039
2040   InitField(x, y, init_game);
2041
2042   // not needed to call InitMovDir() -- already done by InitField()!
2043   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2044       CAN_MOVE(old_element) &&
2045       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2046     InitMovDir(x, y);
2047
2048   /* this case is in fact a combination of not less than three bugs:
2049      first, it calls InitMovDir() for elements that can move, although this is
2050      already done by InitField(); then, it checks the element that was at this
2051      field _before_ the call to InitField() (which can change it); lastly, it
2052      was not called for "mole with direction" elements, which were treated as
2053      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2054   */
2055 }
2056
2057 static int get_key_element_from_nr(int key_nr)
2058 {
2059   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2060                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2061                           EL_EM_KEY_1 : EL_KEY_1);
2062
2063   return key_base_element + key_nr;
2064 }
2065
2066 static int get_next_dropped_element(struct PlayerInfo *player)
2067 {
2068   return (player->inventory_size > 0 ?
2069           player->inventory_element[player->inventory_size - 1] :
2070           player->inventory_infinite_element != EL_UNDEFINED ?
2071           player->inventory_infinite_element :
2072           player->dynabombs_left > 0 ?
2073           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2074           EL_UNDEFINED);
2075 }
2076
2077 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2078 {
2079   // pos >= 0: get element from bottom of the stack;
2080   // pos <  0: get element from top of the stack
2081
2082   if (pos < 0)
2083   {
2084     int min_inventory_size = -pos;
2085     int inventory_pos = player->inventory_size - min_inventory_size;
2086     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2087
2088     return (player->inventory_size >= min_inventory_size ?
2089             player->inventory_element[inventory_pos] :
2090             player->inventory_infinite_element != EL_UNDEFINED ?
2091             player->inventory_infinite_element :
2092             player->dynabombs_left >= min_dynabombs_left ?
2093             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2094             EL_UNDEFINED);
2095   }
2096   else
2097   {
2098     int min_dynabombs_left = pos + 1;
2099     int min_inventory_size = pos + 1 - player->dynabombs_left;
2100     int inventory_pos = pos - player->dynabombs_left;
2101
2102     return (player->inventory_infinite_element != EL_UNDEFINED ?
2103             player->inventory_infinite_element :
2104             player->dynabombs_left >= min_dynabombs_left ?
2105             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2106             player->inventory_size >= min_inventory_size ?
2107             player->inventory_element[inventory_pos] :
2108             EL_UNDEFINED);
2109   }
2110 }
2111
2112 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2113 {
2114   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2115   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2116   int compare_result;
2117
2118   if (gpo1->sort_priority != gpo2->sort_priority)
2119     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2120   else
2121     compare_result = gpo1->nr - gpo2->nr;
2122
2123   return compare_result;
2124 }
2125
2126 int getPlayerInventorySize(int player_nr)
2127 {
2128   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2129     return game_em.ply[player_nr]->dynamite;
2130   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2131     return game_sp.red_disk_count;
2132   else
2133     return stored_player[player_nr].inventory_size;
2134 }
2135
2136 static void InitGameControlValues(void)
2137 {
2138   int i;
2139
2140   for (i = 0; game_panel_controls[i].nr != -1; i++)
2141   {
2142     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2143     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2144     struct TextPosInfo *pos = gpc->pos;
2145     int nr = gpc->nr;
2146     int type = gpc->type;
2147
2148     if (nr != i)
2149     {
2150       Error("'game_panel_controls' structure corrupted at %d", i);
2151
2152       Fail("this should not happen -- please debug");
2153     }
2154
2155     // force update of game controls after initialization
2156     gpc->value = gpc->last_value = -1;
2157     gpc->frame = gpc->last_frame = -1;
2158     gpc->gfx_frame = -1;
2159
2160     // determine panel value width for later calculation of alignment
2161     if (type == TYPE_INTEGER || type == TYPE_STRING)
2162     {
2163       pos->width = pos->size * getFontWidth(pos->font);
2164       pos->height = getFontHeight(pos->font);
2165     }
2166     else if (type == TYPE_ELEMENT)
2167     {
2168       pos->width = pos->size;
2169       pos->height = pos->size;
2170     }
2171
2172     // fill structure for game panel draw order
2173     gpo->nr = gpc->nr;
2174     gpo->sort_priority = pos->sort_priority;
2175   }
2176
2177   // sort game panel controls according to sort_priority and control number
2178   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2179         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2180 }
2181
2182 static void UpdatePlayfieldElementCount(void)
2183 {
2184   boolean use_element_count = FALSE;
2185   int i, j, x, y;
2186
2187   // first check if it is needed at all to calculate playfield element count
2188   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2189     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2190       use_element_count = TRUE;
2191
2192   if (!use_element_count)
2193     return;
2194
2195   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2196     element_info[i].element_count = 0;
2197
2198   SCAN_PLAYFIELD(x, y)
2199   {
2200     element_info[Tile[x][y]].element_count++;
2201   }
2202
2203   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2204     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2205       if (IS_IN_GROUP(j, i))
2206         element_info[EL_GROUP_START + i].element_count +=
2207           element_info[j].element_count;
2208 }
2209
2210 static void UpdateGameControlValues(void)
2211 {
2212   int i, k;
2213   int time = (game.LevelSolved ?
2214               game.LevelSolved_CountingTime :
2215               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2216               game_em.lev->time :
2217               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2218               game_sp.time_played :
2219               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2220               game_mm.energy_left :
2221               game.no_time_limit ? TimePlayed : TimeLeft);
2222   int score = (game.LevelSolved ?
2223                game.LevelSolved_CountingScore :
2224                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2225                game_em.lev->score :
2226                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2227                game_sp.score :
2228                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2229                game_mm.score :
2230                game.score);
2231   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2232               game_em.lev->gems_needed :
2233               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2234               game_sp.infotrons_still_needed :
2235               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2236               game_mm.kettles_still_needed :
2237               game.gems_still_needed);
2238   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2239                      game_em.lev->gems_needed > 0 :
2240                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2241                      game_sp.infotrons_still_needed > 0 :
2242                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2243                      game_mm.kettles_still_needed > 0 ||
2244                      game_mm.lights_still_needed > 0 :
2245                      game.gems_still_needed > 0 ||
2246                      game.sokoban_fields_still_needed > 0 ||
2247                      game.sokoban_objects_still_needed > 0 ||
2248                      game.lights_still_needed > 0);
2249   int health = (game.LevelSolved ?
2250                 game.LevelSolved_CountingHealth :
2251                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2252                 MM_HEALTH(game_mm.laser_overload_value) :
2253                 game.health);
2254   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2255
2256   UpdatePlayfieldElementCount();
2257
2258   // update game panel control values
2259
2260   // used instead of "level_nr" (for network games)
2261   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2262   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2263
2264   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2265   for (i = 0; i < MAX_NUM_KEYS; i++)
2266     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2267   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2268   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2269
2270   if (game.centered_player_nr == -1)
2271   {
2272     for (i = 0; i < MAX_PLAYERS; i++)
2273     {
2274       // only one player in Supaplex game engine
2275       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2276         break;
2277
2278       for (k = 0; k < MAX_NUM_KEYS; k++)
2279       {
2280         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2281         {
2282           if (game_em.ply[i]->keys & (1 << k))
2283             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2284               get_key_element_from_nr(k);
2285         }
2286         else if (stored_player[i].key[k])
2287           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2288             get_key_element_from_nr(k);
2289       }
2290
2291       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2292         getPlayerInventorySize(i);
2293
2294       if (stored_player[i].num_white_keys > 0)
2295         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2296           EL_DC_KEY_WHITE;
2297
2298       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2299         stored_player[i].num_white_keys;
2300     }
2301   }
2302   else
2303   {
2304     int player_nr = game.centered_player_nr;
2305
2306     for (k = 0; k < MAX_NUM_KEYS; k++)
2307     {
2308       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2309       {
2310         if (game_em.ply[player_nr]->keys & (1 << k))
2311           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2312             get_key_element_from_nr(k);
2313       }
2314       else if (stored_player[player_nr].key[k])
2315         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2316           get_key_element_from_nr(k);
2317     }
2318
2319     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2320       getPlayerInventorySize(player_nr);
2321
2322     if (stored_player[player_nr].num_white_keys > 0)
2323       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2324
2325     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2326       stored_player[player_nr].num_white_keys;
2327   }
2328
2329   // re-arrange keys on game panel, if needed or if defined by style settings
2330   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2331   {
2332     int nr = GAME_PANEL_KEY_1 + i;
2333     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2334     struct TextPosInfo *pos = gpc->pos;
2335
2336     // skip check if key is not in the player's inventory
2337     if (gpc->value == EL_EMPTY)
2338       continue;
2339
2340     // check if keys should be arranged on panel from left to right
2341     if (pos->style == STYLE_LEFTMOST_POSITION)
2342     {
2343       // check previous key positions (left from current key)
2344       for (k = 0; k < i; k++)
2345       {
2346         int nr_new = GAME_PANEL_KEY_1 + k;
2347
2348         if (game_panel_controls[nr_new].value == EL_EMPTY)
2349         {
2350           game_panel_controls[nr_new].value = gpc->value;
2351           gpc->value = EL_EMPTY;
2352
2353           break;
2354         }
2355       }
2356     }
2357
2358     // check if "undefined" keys can be placed at some other position
2359     if (pos->x == -1 && pos->y == -1)
2360     {
2361       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2362
2363       // 1st try: display key at the same position as normal or EM keys
2364       if (game_panel_controls[nr_new].value == EL_EMPTY)
2365       {
2366         game_panel_controls[nr_new].value = gpc->value;
2367       }
2368       else
2369       {
2370         // 2nd try: display key at the next free position in the key panel
2371         for (k = 0; k < STD_NUM_KEYS; k++)
2372         {
2373           nr_new = GAME_PANEL_KEY_1 + k;
2374
2375           if (game_panel_controls[nr_new].value == EL_EMPTY)
2376           {
2377             game_panel_controls[nr_new].value = gpc->value;
2378
2379             break;
2380           }
2381         }
2382       }
2383     }
2384   }
2385
2386   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2387   {
2388     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2389       get_inventory_element_from_pos(local_player, i);
2390     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2391       get_inventory_element_from_pos(local_player, -i - 1);
2392   }
2393
2394   game_panel_controls[GAME_PANEL_SCORE].value = score;
2395   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2396
2397   game_panel_controls[GAME_PANEL_TIME].value = time;
2398
2399   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2400   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2401   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2402
2403   if (level.time == 0)
2404     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2405   else
2406     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2407
2408   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2409   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2410
2411   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2412
2413   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2414     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2415      EL_EMPTY);
2416   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2417     local_player->shield_normal_time_left;
2418   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2419     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2420      EL_EMPTY);
2421   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2422     local_player->shield_deadly_time_left;
2423
2424   game_panel_controls[GAME_PANEL_EXIT].value =
2425     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2426
2427   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2428     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2429   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2430     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2431      EL_EMC_MAGIC_BALL_SWITCH);
2432
2433   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2434     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2435   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2436     game.light_time_left;
2437
2438   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2439     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2440   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2441     game.timegate_time_left;
2442
2443   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2444     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2445
2446   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2447     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2448   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2449     game.lenses_time_left;
2450
2451   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2452     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2453   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2454     game.magnify_time_left;
2455
2456   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2457     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2458      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2459      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2460      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2461      EL_BALLOON_SWITCH_NONE);
2462
2463   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2464     local_player->dynabomb_count;
2465   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2466     local_player->dynabomb_size;
2467   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2468     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2469
2470   game_panel_controls[GAME_PANEL_PENGUINS].value =
2471     game.friends_still_needed;
2472
2473   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2474     game.sokoban_objects_still_needed;
2475   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2476     game.sokoban_fields_still_needed;
2477
2478   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2479     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2480
2481   for (i = 0; i < NUM_BELTS; i++)
2482   {
2483     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2484       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2485        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2486     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2487       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2488   }
2489
2490   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2491     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2492   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2493     game.magic_wall_time_left;
2494
2495   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2496     local_player->gravity;
2497
2498   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2499     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2500
2501   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2502     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2503       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2504        game.panel.element[i].id : EL_UNDEFINED);
2505
2506   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2507     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2508       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2509        element_info[game.panel.element_count[i].id].element_count : 0);
2510
2511   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2512     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2513       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2514        element_info[game.panel.ce_score[i].id].collect_score : 0);
2515
2516   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2517     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2518       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2519        element_info[game.panel.ce_score_element[i].id].collect_score :
2520        EL_UNDEFINED);
2521
2522   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2523   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2524   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2525
2526   // update game panel control frames
2527
2528   for (i = 0; game_panel_controls[i].nr != -1; i++)
2529   {
2530     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2531
2532     if (gpc->type == TYPE_ELEMENT)
2533     {
2534       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2535       {
2536         int last_anim_random_frame = gfx.anim_random_frame;
2537         int element = gpc->value;
2538         int graphic = el2panelimg(element);
2539         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2540                                sync_random_frame : INIT_GFX_RANDOM());
2541
2542         if (gpc->value != gpc->last_value)
2543         {
2544           gpc->gfx_frame = 0;
2545           gpc->gfx_random = init_gfx_random;
2546         }
2547         else
2548         {
2549           gpc->gfx_frame++;
2550
2551           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2552               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2553             gpc->gfx_random = init_gfx_random;
2554         }
2555
2556         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2557           gfx.anim_random_frame = gpc->gfx_random;
2558
2559         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2560           gpc->gfx_frame = element_info[element].collect_score;
2561
2562         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2563
2564         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2565           gfx.anim_random_frame = last_anim_random_frame;
2566       }
2567     }
2568     else if (gpc->type == TYPE_GRAPHIC)
2569     {
2570       if (gpc->graphic != IMG_UNDEFINED)
2571       {
2572         int last_anim_random_frame = gfx.anim_random_frame;
2573         int graphic = gpc->graphic;
2574         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2575                                sync_random_frame : INIT_GFX_RANDOM());
2576
2577         if (gpc->value != gpc->last_value)
2578         {
2579           gpc->gfx_frame = 0;
2580           gpc->gfx_random = init_gfx_random;
2581         }
2582         else
2583         {
2584           gpc->gfx_frame++;
2585
2586           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2587               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2588             gpc->gfx_random = init_gfx_random;
2589         }
2590
2591         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2592           gfx.anim_random_frame = gpc->gfx_random;
2593
2594         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2595
2596         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2597           gfx.anim_random_frame = last_anim_random_frame;
2598       }
2599     }
2600   }
2601 }
2602
2603 static void DisplayGameControlValues(void)
2604 {
2605   boolean redraw_panel = FALSE;
2606   int i;
2607
2608   for (i = 0; game_panel_controls[i].nr != -1; i++)
2609   {
2610     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2611
2612     if (PANEL_DEACTIVATED(gpc->pos))
2613       continue;
2614
2615     if (gpc->value == gpc->last_value &&
2616         gpc->frame == gpc->last_frame)
2617       continue;
2618
2619     redraw_panel = TRUE;
2620   }
2621
2622   if (!redraw_panel)
2623     return;
2624
2625   // copy default game door content to main double buffer
2626
2627   // !!! CHECK AGAIN !!!
2628   SetPanelBackground();
2629   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2630   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2631
2632   // redraw game control buttons
2633   RedrawGameButtons();
2634
2635   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2636
2637   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2638   {
2639     int nr = game_panel_order[i].nr;
2640     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2641     struct TextPosInfo *pos = gpc->pos;
2642     int type = gpc->type;
2643     int value = gpc->value;
2644     int frame = gpc->frame;
2645     int size = pos->size;
2646     int font = pos->font;
2647     boolean draw_masked = pos->draw_masked;
2648     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2649
2650     if (PANEL_DEACTIVATED(pos))
2651       continue;
2652
2653     if (pos->class == get_hash_from_key("extra_panel_items") &&
2654         !setup.prefer_extra_panel_items)
2655       continue;
2656
2657     gpc->last_value = value;
2658     gpc->last_frame = frame;
2659
2660     if (type == TYPE_INTEGER)
2661     {
2662       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2663           nr == GAME_PANEL_TIME)
2664       {
2665         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2666
2667         if (use_dynamic_size)           // use dynamic number of digits
2668         {
2669           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2670           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2671           int size2 = size1 + 1;
2672           int font1 = pos->font;
2673           int font2 = pos->font_alt;
2674
2675           size = (value < value_change ? size1 : size2);
2676           font = (value < value_change ? font1 : font2);
2677         }
2678       }
2679
2680       // correct text size if "digits" is zero or less
2681       if (size <= 0)
2682         size = strlen(int2str(value, size));
2683
2684       // dynamically correct text alignment
2685       pos->width = size * getFontWidth(font);
2686
2687       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2688                   int2str(value, size), font, mask_mode);
2689     }
2690     else if (type == TYPE_ELEMENT)
2691     {
2692       int element, graphic;
2693       Bitmap *src_bitmap;
2694       int src_x, src_y;
2695       int width, height;
2696       int dst_x = PANEL_XPOS(pos);
2697       int dst_y = PANEL_YPOS(pos);
2698
2699       if (value != EL_UNDEFINED && value != EL_EMPTY)
2700       {
2701         element = value;
2702         graphic = el2panelimg(value);
2703
2704 #if 0
2705         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2706               element, EL_NAME(element), size);
2707 #endif
2708
2709         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2710           size = TILESIZE;
2711
2712         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2713                               &src_x, &src_y);
2714
2715         width  = graphic_info[graphic].width  * size / TILESIZE;
2716         height = graphic_info[graphic].height * size / TILESIZE;
2717
2718         if (draw_masked)
2719           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2720                            dst_x, dst_y);
2721         else
2722           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2723                      dst_x, dst_y);
2724       }
2725     }
2726     else if (type == TYPE_GRAPHIC)
2727     {
2728       int graphic        = gpc->graphic;
2729       int graphic_active = gpc->graphic_active;
2730       Bitmap *src_bitmap;
2731       int src_x, src_y;
2732       int width, height;
2733       int dst_x = PANEL_XPOS(pos);
2734       int dst_y = PANEL_YPOS(pos);
2735       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2736                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2737
2738       if (graphic != IMG_UNDEFINED && !skip)
2739       {
2740         if (pos->style == STYLE_REVERSE)
2741           value = 100 - value;
2742
2743         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2744
2745         if (pos->direction & MV_HORIZONTAL)
2746         {
2747           width  = graphic_info[graphic_active].width * value / 100;
2748           height = graphic_info[graphic_active].height;
2749
2750           if (pos->direction == MV_LEFT)
2751           {
2752             src_x += graphic_info[graphic_active].width - width;
2753             dst_x += graphic_info[graphic_active].width - width;
2754           }
2755         }
2756         else
2757         {
2758           width  = graphic_info[graphic_active].width;
2759           height = graphic_info[graphic_active].height * value / 100;
2760
2761           if (pos->direction == MV_UP)
2762           {
2763             src_y += graphic_info[graphic_active].height - height;
2764             dst_y += graphic_info[graphic_active].height - height;
2765           }
2766         }
2767
2768         if (draw_masked)
2769           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2770                            dst_x, dst_y);
2771         else
2772           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2773                      dst_x, dst_y);
2774
2775         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2776
2777         if (pos->direction & MV_HORIZONTAL)
2778         {
2779           if (pos->direction == MV_RIGHT)
2780           {
2781             src_x += width;
2782             dst_x += width;
2783           }
2784           else
2785           {
2786             dst_x = PANEL_XPOS(pos);
2787           }
2788
2789           width = graphic_info[graphic].width - width;
2790         }
2791         else
2792         {
2793           if (pos->direction == MV_DOWN)
2794           {
2795             src_y += height;
2796             dst_y += height;
2797           }
2798           else
2799           {
2800             dst_y = PANEL_YPOS(pos);
2801           }
2802
2803           height = graphic_info[graphic].height - height;
2804         }
2805
2806         if (draw_masked)
2807           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2808                            dst_x, dst_y);
2809         else
2810           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2811                      dst_x, dst_y);
2812       }
2813     }
2814     else if (type == TYPE_STRING)
2815     {
2816       boolean active = (value != 0);
2817       char *state_normal = "off";
2818       char *state_active = "on";
2819       char *state = (active ? state_active : state_normal);
2820       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2821                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2822                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2823                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2824
2825       if (nr == GAME_PANEL_GRAVITY_STATE)
2826       {
2827         int font1 = pos->font;          // (used for normal state)
2828         int font2 = pos->font_alt;      // (used for active state)
2829
2830         font = (active ? font2 : font1);
2831       }
2832
2833       if (s != NULL)
2834       {
2835         char *s_cut;
2836
2837         if (size <= 0)
2838         {
2839           // don't truncate output if "chars" is zero or less
2840           size = strlen(s);
2841
2842           // dynamically correct text alignment
2843           pos->width = size * getFontWidth(font);
2844         }
2845
2846         s_cut = getStringCopyN(s, size);
2847
2848         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2849                     s_cut, font, mask_mode);
2850
2851         free(s_cut);
2852       }
2853     }
2854
2855     redraw_mask |= REDRAW_DOOR_1;
2856   }
2857
2858   SetGameStatus(GAME_MODE_PLAYING);
2859 }
2860
2861 void UpdateAndDisplayGameControlValues(void)
2862 {
2863   if (tape.deactivate_display)
2864     return;
2865
2866   UpdateGameControlValues();
2867   DisplayGameControlValues();
2868 }
2869
2870 #if 0
2871 static void UpdateGameDoorValues(void)
2872 {
2873   UpdateGameControlValues();
2874 }
2875 #endif
2876
2877 void DrawGameDoorValues(void)
2878 {
2879   DisplayGameControlValues();
2880 }
2881
2882
2883 // ============================================================================
2884 // InitGameEngine()
2885 // ----------------------------------------------------------------------------
2886 // initialize game engine due to level / tape version number
2887 // ============================================================================
2888
2889 static void InitGameEngine(void)
2890 {
2891   int i, j, k, l, x, y;
2892
2893   // set game engine from tape file when re-playing, else from level file
2894   game.engine_version = (tape.playing ? tape.engine_version :
2895                          level.game_version);
2896
2897   // set single or multi-player game mode (needed for re-playing tapes)
2898   game.team_mode = setup.team_mode;
2899
2900   if (tape.playing)
2901   {
2902     int num_players = 0;
2903
2904     for (i = 0; i < MAX_PLAYERS; i++)
2905       if (tape.player_participates[i])
2906         num_players++;
2907
2908     // multi-player tapes contain input data for more than one player
2909     game.team_mode = (num_players > 1);
2910   }
2911
2912 #if 0
2913   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2914         level.game_version);
2915   Debug("game:init:level", "          tape.file_version   == %06d",
2916         tape.file_version);
2917   Debug("game:init:level", "          tape.game_version   == %06d",
2918         tape.game_version);
2919   Debug("game:init:level", "          tape.engine_version == %06d",
2920         tape.engine_version);
2921   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2922         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2923 #endif
2924
2925   // --------------------------------------------------------------------------
2926   // set flags for bugs and changes according to active game engine version
2927   // --------------------------------------------------------------------------
2928
2929   /*
2930     Summary of bugfix:
2931     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2932
2933     Bug was introduced in version:
2934     2.0.1
2935
2936     Bug was fixed in version:
2937     4.2.0.0
2938
2939     Description:
2940     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2941     but the property "can fall" was missing, which caused some levels to be
2942     unsolvable. This was fixed in version 4.2.0.0.
2943
2944     Affected levels/tapes:
2945     An example for a tape that was fixed by this bugfix is tape 029 from the
2946     level set "rnd_sam_bateman".
2947     The wrong behaviour will still be used for all levels or tapes that were
2948     created/recorded with it. An example for this is tape 023 from the level
2949     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2950   */
2951
2952   boolean use_amoeba_dropping_cannot_fall_bug =
2953     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2954       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2955      (tape.playing &&
2956       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2957       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2958
2959   /*
2960     Summary of bugfix/change:
2961     Fixed move speed of elements entering or leaving magic wall.
2962
2963     Fixed/changed in version:
2964     2.0.1
2965
2966     Description:
2967     Before 2.0.1, move speed of elements entering or leaving magic wall was
2968     twice as fast as it is now.
2969     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2970
2971     Affected levels/tapes:
2972     The first condition is generally needed for all levels/tapes before version
2973     2.0.1, which might use the old behaviour before it was changed; known tapes
2974     that are affected: Tape 014 from the level set "rnd_conor_mancone".
2975     The second condition is an exception from the above case and is needed for
2976     the special case of tapes recorded with game (not engine!) version 2.0.1 or
2977     above, but before it was known that this change would break tapes like the
2978     above and was fixed in 4.2.0.0, so that the changed behaviour was active
2979     although the engine version while recording maybe was before 2.0.1. There
2980     are a lot of tapes that are affected by this exception, like tape 006 from
2981     the level set "rnd_conor_mancone".
2982   */
2983
2984   boolean use_old_move_stepsize_for_magic_wall =
2985     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
2986      !(tape.playing &&
2987        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2988        tape.game_version <  VERSION_IDENT(4,2,0,0)));
2989
2990   /*
2991     Summary of bugfix/change:
2992     Fixed handling for custom elements that change when pushed by the player.
2993
2994     Fixed/changed in version:
2995     3.1.0
2996
2997     Description:
2998     Before 3.1.0, custom elements that "change when pushing" changed directly
2999     after the player started pushing them (until then handled in "DigField()").
3000     Since 3.1.0, these custom elements are not changed until the "pushing"
3001     move of the element is finished (now handled in "ContinueMoving()").
3002
3003     Affected levels/tapes:
3004     The first condition is generally needed for all levels/tapes before version
3005     3.1.0, which might use the old behaviour before it was changed; known tapes
3006     that are affected are some tapes from the level set "Walpurgis Gardens" by
3007     Jamie Cullen.
3008     The second condition is an exception from the above case and is needed for
3009     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3010     above (including some development versions of 3.1.0), but before it was
3011     known that this change would break tapes like the above and was fixed in
3012     3.1.1, so that the changed behaviour was active although the engine version
3013     while recording maybe was before 3.1.0. There is at least one tape that is
3014     affected by this exception, which is the tape for the one-level set "Bug
3015     Machine" by Juergen Bonhagen.
3016   */
3017
3018   game.use_change_when_pushing_bug =
3019     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3020      !(tape.playing &&
3021        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3022        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3023
3024   /*
3025     Summary of bugfix/change:
3026     Fixed handling for blocking the field the player leaves when moving.
3027
3028     Fixed/changed in version:
3029     3.1.1
3030
3031     Description:
3032     Before 3.1.1, when "block last field when moving" was enabled, the field
3033     the player is leaving when moving was blocked for the time of the move,
3034     and was directly unblocked afterwards. This resulted in the last field
3035     being blocked for exactly one less than the number of frames of one player
3036     move. Additionally, even when blocking was disabled, the last field was
3037     blocked for exactly one frame.
3038     Since 3.1.1, due to changes in player movement handling, the last field
3039     is not blocked at all when blocking is disabled. When blocking is enabled,
3040     the last field is blocked for exactly the number of frames of one player
3041     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3042     last field is blocked for exactly one more than the number of frames of
3043     one player move.
3044
3045     Affected levels/tapes:
3046     (!!! yet to be determined -- probably many !!!)
3047   */
3048
3049   game.use_block_last_field_bug =
3050     (game.engine_version < VERSION_IDENT(3,1,1,0));
3051
3052   /* various special flags and settings for native Emerald Mine game engine */
3053
3054   game_em.use_single_button =
3055     (game.engine_version > VERSION_IDENT(4,0,0,2));
3056
3057   game_em.use_snap_key_bug =
3058     (game.engine_version < VERSION_IDENT(4,0,1,0));
3059
3060   game_em.use_random_bug =
3061     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3062
3063   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3064
3065   game_em.use_old_explosions            = use_old_em_engine;
3066   game_em.use_old_android               = use_old_em_engine;
3067   game_em.use_old_push_elements         = use_old_em_engine;
3068   game_em.use_old_push_into_acid        = use_old_em_engine;
3069
3070   game_em.use_wrap_around               = !use_old_em_engine;
3071
3072   // --------------------------------------------------------------------------
3073
3074   // set maximal allowed number of custom element changes per game frame
3075   game.max_num_changes_per_frame = 1;
3076
3077   // default scan direction: scan playfield from top/left to bottom/right
3078   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3079
3080   // dynamically adjust element properties according to game engine version
3081   InitElementPropertiesEngine(game.engine_version);
3082
3083   // ---------- initialize special element properties -------------------------
3084
3085   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3086   if (use_amoeba_dropping_cannot_fall_bug)
3087     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3088
3089   // ---------- initialize player's initial move delay ------------------------
3090
3091   // dynamically adjust player properties according to level information
3092   for (i = 0; i < MAX_PLAYERS; i++)
3093     game.initial_move_delay_value[i] =
3094       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3095
3096   // dynamically adjust player properties according to game engine version
3097   for (i = 0; i < MAX_PLAYERS; i++)
3098     game.initial_move_delay[i] =
3099       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3100        game.initial_move_delay_value[i] : 0);
3101
3102   // ---------- initialize player's initial push delay ------------------------
3103
3104   // dynamically adjust player properties according to game engine version
3105   game.initial_push_delay_value =
3106     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3107
3108   // ---------- initialize changing elements ----------------------------------
3109
3110   // initialize changing elements information
3111   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3112   {
3113     struct ElementInfo *ei = &element_info[i];
3114
3115     // this pointer might have been changed in the level editor
3116     ei->change = &ei->change_page[0];
3117
3118     if (!IS_CUSTOM_ELEMENT(i))
3119     {
3120       ei->change->target_element = EL_EMPTY_SPACE;
3121       ei->change->delay_fixed = 0;
3122       ei->change->delay_random = 0;
3123       ei->change->delay_frames = 1;
3124     }
3125
3126     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3127     {
3128       ei->has_change_event[j] = FALSE;
3129
3130       ei->event_page_nr[j] = 0;
3131       ei->event_page[j] = &ei->change_page[0];
3132     }
3133   }
3134
3135   // add changing elements from pre-defined list
3136   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3137   {
3138     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3139     struct ElementInfo *ei = &element_info[ch_delay->element];
3140
3141     ei->change->target_element       = ch_delay->target_element;
3142     ei->change->delay_fixed          = ch_delay->change_delay;
3143
3144     ei->change->pre_change_function  = ch_delay->pre_change_function;
3145     ei->change->change_function      = ch_delay->change_function;
3146     ei->change->post_change_function = ch_delay->post_change_function;
3147
3148     ei->change->can_change = TRUE;
3149     ei->change->can_change_or_has_action = TRUE;
3150
3151     ei->has_change_event[CE_DELAY] = TRUE;
3152
3153     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3154     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3155   }
3156
3157   // ---------- initialize internal run-time variables ------------------------
3158
3159   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3160   {
3161     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3162
3163     for (j = 0; j < ei->num_change_pages; j++)
3164     {
3165       ei->change_page[j].can_change_or_has_action =
3166         (ei->change_page[j].can_change |
3167          ei->change_page[j].has_action);
3168     }
3169   }
3170
3171   // add change events from custom element configuration
3172   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3173   {
3174     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3175
3176     for (j = 0; j < ei->num_change_pages; j++)
3177     {
3178       if (!ei->change_page[j].can_change_or_has_action)
3179         continue;
3180
3181       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3182       {
3183         // only add event page for the first page found with this event
3184         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3185         {
3186           ei->has_change_event[k] = TRUE;
3187
3188           ei->event_page_nr[k] = j;
3189           ei->event_page[k] = &ei->change_page[j];
3190         }
3191       }
3192     }
3193   }
3194
3195   // ---------- initialize reference elements in change conditions ------------
3196
3197   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3198   {
3199     int element = EL_CUSTOM_START + i;
3200     struct ElementInfo *ei = &element_info[element];
3201
3202     for (j = 0; j < ei->num_change_pages; j++)
3203     {
3204       int trigger_element = ei->change_page[j].initial_trigger_element;
3205
3206       if (trigger_element >= EL_PREV_CE_8 &&
3207           trigger_element <= EL_NEXT_CE_8)
3208         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3209
3210       ei->change_page[j].trigger_element = trigger_element;
3211     }
3212   }
3213
3214   // ---------- initialize run-time trigger player and element ----------------
3215
3216   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3217   {
3218     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3219
3220     for (j = 0; j < ei->num_change_pages; j++)
3221     {
3222       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3223       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3224       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3225       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3226       ei->change_page[j].actual_trigger_ce_value = 0;
3227       ei->change_page[j].actual_trigger_ce_score = 0;
3228     }
3229   }
3230
3231   // ---------- initialize trigger events -------------------------------------
3232
3233   // initialize trigger events information
3234   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3235     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3236       trigger_events[i][j] = FALSE;
3237
3238   // add trigger events from element change event properties
3239   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3240   {
3241     struct ElementInfo *ei = &element_info[i];
3242
3243     for (j = 0; j < ei->num_change_pages; j++)
3244     {
3245       if (!ei->change_page[j].can_change_or_has_action)
3246         continue;
3247
3248       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3249       {
3250         int trigger_element = ei->change_page[j].trigger_element;
3251
3252         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3253         {
3254           if (ei->change_page[j].has_event[k])
3255           {
3256             if (IS_GROUP_ELEMENT(trigger_element))
3257             {
3258               struct ElementGroupInfo *group =
3259                 element_info[trigger_element].group;
3260
3261               for (l = 0; l < group->num_elements_resolved; l++)
3262                 trigger_events[group->element_resolved[l]][k] = TRUE;
3263             }
3264             else if (trigger_element == EL_ANY_ELEMENT)
3265               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3266                 trigger_events[l][k] = TRUE;
3267             else
3268               trigger_events[trigger_element][k] = TRUE;
3269           }
3270         }
3271       }
3272     }
3273   }
3274
3275   // ---------- initialize push delay -----------------------------------------
3276
3277   // initialize push delay values to default
3278   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3279   {
3280     if (!IS_CUSTOM_ELEMENT(i))
3281     {
3282       // set default push delay values (corrected since version 3.0.7-1)
3283       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3284       {
3285         element_info[i].push_delay_fixed = 2;
3286         element_info[i].push_delay_random = 8;
3287       }
3288       else
3289       {
3290         element_info[i].push_delay_fixed = 8;
3291         element_info[i].push_delay_random = 8;
3292       }
3293     }
3294   }
3295
3296   // set push delay value for certain elements from pre-defined list
3297   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3298   {
3299     int e = push_delay_list[i].element;
3300
3301     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3302     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3303   }
3304
3305   // set push delay value for Supaplex elements for newer engine versions
3306   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3307   {
3308     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3309     {
3310       if (IS_SP_ELEMENT(i))
3311       {
3312         // set SP push delay to just enough to push under a falling zonk
3313         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3314
3315         element_info[i].push_delay_fixed  = delay;
3316         element_info[i].push_delay_random = 0;
3317       }
3318     }
3319   }
3320
3321   // ---------- initialize move stepsize --------------------------------------
3322
3323   // initialize move stepsize values to default
3324   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3325     if (!IS_CUSTOM_ELEMENT(i))
3326       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3327
3328   // set move stepsize value for certain elements from pre-defined list
3329   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3330   {
3331     int e = move_stepsize_list[i].element;
3332
3333     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3334
3335     // set move stepsize value for certain elements for older engine versions
3336     if (use_old_move_stepsize_for_magic_wall)
3337     {
3338       if (e == EL_MAGIC_WALL_FILLING ||
3339           e == EL_MAGIC_WALL_EMPTYING ||
3340           e == EL_BD_MAGIC_WALL_FILLING ||
3341           e == EL_BD_MAGIC_WALL_EMPTYING)
3342         element_info[e].move_stepsize *= 2;
3343     }
3344   }
3345
3346   // ---------- initialize collect score --------------------------------------
3347
3348   // initialize collect score values for custom elements from initial value
3349   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3350     if (IS_CUSTOM_ELEMENT(i))
3351       element_info[i].collect_score = element_info[i].collect_score_initial;
3352
3353   // ---------- initialize collect count --------------------------------------
3354
3355   // initialize collect count values for non-custom elements
3356   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3357     if (!IS_CUSTOM_ELEMENT(i))
3358       element_info[i].collect_count_initial = 0;
3359
3360   // add collect count values for all elements from pre-defined list
3361   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3362     element_info[collect_count_list[i].element].collect_count_initial =
3363       collect_count_list[i].count;
3364
3365   // ---------- initialize access direction -----------------------------------
3366
3367   // initialize access direction values to default (access from every side)
3368   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3369     if (!IS_CUSTOM_ELEMENT(i))
3370       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3371
3372   // set access direction value for certain elements from pre-defined list
3373   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3374     element_info[access_direction_list[i].element].access_direction =
3375       access_direction_list[i].direction;
3376
3377   // ---------- initialize explosion content ----------------------------------
3378   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3379   {
3380     if (IS_CUSTOM_ELEMENT(i))
3381       continue;
3382
3383     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3384     {
3385       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3386
3387       element_info[i].content.e[x][y] =
3388         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3389          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3390          i == EL_PLAYER_3 ? EL_EMERALD :
3391          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3392          i == EL_MOLE ? EL_EMERALD_RED :
3393          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3394          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3395          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3396          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3397          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3398          i == EL_WALL_EMERALD ? EL_EMERALD :
3399          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3400          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3401          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3402          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3403          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3404          i == EL_WALL_PEARL ? EL_PEARL :
3405          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3406          EL_EMPTY);
3407     }
3408   }
3409
3410   // ---------- initialize recursion detection --------------------------------
3411   recursion_loop_depth = 0;
3412   recursion_loop_detected = FALSE;
3413   recursion_loop_element = EL_UNDEFINED;
3414
3415   // ---------- initialize graphics engine ------------------------------------
3416   game.scroll_delay_value =
3417     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3418      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3419      !setup.forced_scroll_delay           ? 0 :
3420      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3421   game.scroll_delay_value =
3422     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3423
3424   // ---------- initialize game engine snapshots ------------------------------
3425   for (i = 0; i < MAX_PLAYERS; i++)
3426     game.snapshot.last_action[i] = 0;
3427   game.snapshot.changed_action = FALSE;
3428   game.snapshot.collected_item = FALSE;
3429   game.snapshot.mode =
3430     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3431      SNAPSHOT_MODE_EVERY_STEP :
3432      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3433      SNAPSHOT_MODE_EVERY_MOVE :
3434      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3435      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3436   game.snapshot.save_snapshot = FALSE;
3437
3438   // ---------- initialize level time for Supaplex engine ---------------------
3439   // Supaplex levels with time limit currently unsupported -- should be added
3440   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3441     level.time = 0;
3442
3443   // ---------- initialize flags for handling game actions --------------------
3444
3445   // set flags for game actions to default values
3446   game.use_key_actions = TRUE;
3447   game.use_mouse_actions = FALSE;
3448
3449   // when using Mirror Magic game engine, handle mouse events only
3450   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3451   {
3452     game.use_key_actions = FALSE;
3453     game.use_mouse_actions = TRUE;
3454   }
3455
3456   // check for custom elements with mouse click events
3457   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3458   {
3459     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3460     {
3461       int element = EL_CUSTOM_START + i;
3462
3463       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3464           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3465           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3466           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3467         game.use_mouse_actions = TRUE;
3468     }
3469   }
3470 }
3471
3472 static int get_num_special_action(int element, int action_first,
3473                                   int action_last)
3474 {
3475   int num_special_action = 0;
3476   int i, j;
3477
3478   for (i = action_first; i <= action_last; i++)
3479   {
3480     boolean found = FALSE;
3481
3482     for (j = 0; j < NUM_DIRECTIONS; j++)
3483       if (el_act_dir2img(element, i, j) !=
3484           el_act_dir2img(element, ACTION_DEFAULT, j))
3485         found = TRUE;
3486
3487     if (found)
3488       num_special_action++;
3489     else
3490       break;
3491   }
3492
3493   return num_special_action;
3494 }
3495
3496
3497 // ============================================================================
3498 // InitGame()
3499 // ----------------------------------------------------------------------------
3500 // initialize and start new game
3501 // ============================================================================
3502
3503 #if DEBUG_INIT_PLAYER
3504 static void DebugPrintPlayerStatus(char *message)
3505 {
3506   int i;
3507
3508   if (!options.debug)
3509     return;
3510
3511   Debug("game:init:player", "%s:", message);
3512
3513   for (i = 0; i < MAX_PLAYERS; i++)
3514   {
3515     struct PlayerInfo *player = &stored_player[i];
3516
3517     Debug("game:init:player",
3518           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3519           i + 1,
3520           player->present,
3521           player->connected,
3522           player->connected_locally,
3523           player->connected_network,
3524           player->active,
3525           (local_player == player ? " (local player)" : ""));
3526   }
3527 }
3528 #endif
3529
3530 void InitGame(void)
3531 {
3532   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3533   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3534   int fade_mask = REDRAW_FIELD;
3535
3536   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3537   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3538   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3539   int initial_move_dir = MV_DOWN;
3540   int i, j, x, y;
3541
3542   // required here to update video display before fading (FIX THIS)
3543   DrawMaskedBorder(REDRAW_DOOR_2);
3544
3545   if (!game.restart_level)
3546     CloseDoor(DOOR_CLOSE_1);
3547
3548   SetGameStatus(GAME_MODE_PLAYING);
3549
3550   if (level_editor_test_game)
3551     FadeSkipNextFadeOut();
3552   else
3553     FadeSetEnterScreen();
3554
3555   if (CheckFadeAll())
3556     fade_mask = REDRAW_ALL;
3557
3558   FadeLevelSoundsAndMusic();
3559
3560   ExpireSoundLoops(TRUE);
3561
3562   FadeOut(fade_mask);
3563
3564   if (level_editor_test_game)
3565     FadeSkipNextFadeIn();
3566
3567   // needed if different viewport properties defined for playing
3568   ChangeViewportPropertiesIfNeeded();
3569
3570   ClearField();
3571
3572   DrawCompleteVideoDisplay();
3573
3574   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3575
3576   InitGameEngine();
3577   InitGameControlValues();
3578
3579   if (tape.recording)
3580   {
3581     // initialize tape actions from game when recording tape
3582     tape.use_key_actions   = game.use_key_actions;
3583     tape.use_mouse_actions = game.use_mouse_actions;
3584
3585     // initialize visible playfield size when recording tape (for team mode)
3586     tape.scr_fieldx = SCR_FIELDX;
3587     tape.scr_fieldy = SCR_FIELDY;
3588   }
3589
3590   // don't play tapes over network
3591   network_playing = (network.enabled && !tape.playing);
3592
3593   for (i = 0; i < MAX_PLAYERS; i++)
3594   {
3595     struct PlayerInfo *player = &stored_player[i];
3596
3597     player->index_nr = i;
3598     player->index_bit = (1 << i);
3599     player->element_nr = EL_PLAYER_1 + i;
3600
3601     player->present = FALSE;
3602     player->active = FALSE;
3603     player->mapped = FALSE;
3604
3605     player->killed = FALSE;
3606     player->reanimated = FALSE;
3607     player->buried = FALSE;
3608
3609     player->action = 0;
3610     player->effective_action = 0;
3611     player->programmed_action = 0;
3612     player->snap_action = 0;
3613
3614     player->mouse_action.lx = 0;
3615     player->mouse_action.ly = 0;
3616     player->mouse_action.button = 0;
3617     player->mouse_action.button_hint = 0;
3618
3619     player->effective_mouse_action.lx = 0;
3620     player->effective_mouse_action.ly = 0;
3621     player->effective_mouse_action.button = 0;
3622     player->effective_mouse_action.button_hint = 0;
3623
3624     for (j = 0; j < MAX_NUM_KEYS; j++)
3625       player->key[j] = FALSE;
3626
3627     player->num_white_keys = 0;
3628
3629     player->dynabomb_count = 0;
3630     player->dynabomb_size = 1;
3631     player->dynabombs_left = 0;
3632     player->dynabomb_xl = FALSE;
3633
3634     player->MovDir = initial_move_dir;
3635     player->MovPos = 0;
3636     player->GfxPos = 0;
3637     player->GfxDir = initial_move_dir;
3638     player->GfxAction = ACTION_DEFAULT;
3639     player->Frame = 0;
3640     player->StepFrame = 0;
3641
3642     player->initial_element = player->element_nr;
3643     player->artwork_element =
3644       (level.use_artwork_element[i] ? level.artwork_element[i] :
3645        player->element_nr);
3646     player->use_murphy = FALSE;
3647
3648     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3649     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3650
3651     player->gravity = level.initial_player_gravity[i];
3652
3653     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3654
3655     player->actual_frame_counter = 0;
3656
3657     player->step_counter = 0;
3658
3659     player->last_move_dir = initial_move_dir;
3660
3661     player->is_active = FALSE;
3662
3663     player->is_waiting = FALSE;
3664     player->is_moving = FALSE;
3665     player->is_auto_moving = FALSE;
3666     player->is_digging = FALSE;
3667     player->is_snapping = FALSE;
3668     player->is_collecting = FALSE;
3669     player->is_pushing = FALSE;
3670     player->is_switching = FALSE;
3671     player->is_dropping = FALSE;
3672     player->is_dropping_pressed = FALSE;
3673
3674     player->is_bored = FALSE;
3675     player->is_sleeping = FALSE;
3676
3677     player->was_waiting = TRUE;
3678     player->was_moving = FALSE;
3679     player->was_snapping = FALSE;
3680     player->was_dropping = FALSE;
3681
3682     player->force_dropping = FALSE;
3683
3684     player->frame_counter_bored = -1;
3685     player->frame_counter_sleeping = -1;
3686
3687     player->anim_delay_counter = 0;
3688     player->post_delay_counter = 0;
3689
3690     player->dir_waiting = initial_move_dir;
3691     player->action_waiting = ACTION_DEFAULT;
3692     player->last_action_waiting = ACTION_DEFAULT;
3693     player->special_action_bored = ACTION_DEFAULT;
3694     player->special_action_sleeping = ACTION_DEFAULT;
3695
3696     player->switch_x = -1;
3697     player->switch_y = -1;
3698
3699     player->drop_x = -1;
3700     player->drop_y = -1;
3701
3702     player->show_envelope = 0;
3703
3704     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3705
3706     player->push_delay       = -1;      // initialized when pushing starts
3707     player->push_delay_value = game.initial_push_delay_value;
3708
3709     player->drop_delay = 0;
3710     player->drop_pressed_delay = 0;
3711
3712     player->last_jx = -1;
3713     player->last_jy = -1;
3714     player->jx = -1;
3715     player->jy = -1;
3716
3717     player->shield_normal_time_left = 0;
3718     player->shield_deadly_time_left = 0;
3719
3720     player->inventory_infinite_element = EL_UNDEFINED;
3721     player->inventory_size = 0;
3722
3723     if (level.use_initial_inventory[i])
3724     {
3725       for (j = 0; j < level.initial_inventory_size[i]; j++)
3726       {
3727         int element = level.initial_inventory_content[i][j];
3728         int collect_count = element_info[element].collect_count_initial;
3729         int k;
3730
3731         if (!IS_CUSTOM_ELEMENT(element))
3732           collect_count = 1;
3733
3734         if (collect_count == 0)
3735           player->inventory_infinite_element = element;
3736         else
3737           for (k = 0; k < collect_count; k++)
3738             if (player->inventory_size < MAX_INVENTORY_SIZE)
3739               player->inventory_element[player->inventory_size++] = element;
3740       }
3741     }
3742
3743     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3744     SnapField(player, 0, 0);
3745
3746     map_player_action[i] = i;
3747   }
3748
3749   network_player_action_received = FALSE;
3750
3751   // initial null action
3752   if (network_playing)
3753     SendToServer_MovePlayer(MV_NONE);
3754
3755   FrameCounter = 0;
3756   TimeFrames = 0;
3757   TimePlayed = 0;
3758   TimeLeft = level.time;
3759   TapeTime = 0;
3760
3761   ScreenMovDir = MV_NONE;
3762   ScreenMovPos = 0;
3763   ScreenGfxPos = 0;
3764
3765   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3766
3767   game.robot_wheel_x = -1;
3768   game.robot_wheel_y = -1;
3769
3770   game.exit_x = -1;
3771   game.exit_y = -1;
3772
3773   game.all_players_gone = FALSE;
3774
3775   game.LevelSolved = FALSE;
3776   game.GameOver = FALSE;
3777
3778   game.GamePlayed = !tape.playing;
3779
3780   game.LevelSolved_GameWon = FALSE;
3781   game.LevelSolved_GameEnd = FALSE;
3782   game.LevelSolved_SaveTape = FALSE;
3783   game.LevelSolved_SaveScore = FALSE;
3784
3785   game.LevelSolved_CountingTime = 0;
3786   game.LevelSolved_CountingScore = 0;
3787   game.LevelSolved_CountingHealth = 0;
3788
3789   game.panel.active = TRUE;
3790
3791   game.no_time_limit = (level.time == 0);
3792
3793   game.yamyam_content_nr = 0;
3794   game.robot_wheel_active = FALSE;
3795   game.magic_wall_active = FALSE;
3796   game.magic_wall_time_left = 0;
3797   game.light_time_left = 0;
3798   game.timegate_time_left = 0;
3799   game.switchgate_pos = 0;
3800   game.wind_direction = level.wind_direction_initial;
3801
3802   game.score = 0;
3803   game.score_final = 0;
3804
3805   game.health = MAX_HEALTH;
3806   game.health_final = MAX_HEALTH;
3807
3808   game.gems_still_needed = level.gems_needed;
3809   game.sokoban_fields_still_needed = 0;
3810   game.sokoban_objects_still_needed = 0;
3811   game.lights_still_needed = 0;
3812   game.players_still_needed = 0;
3813   game.friends_still_needed = 0;
3814
3815   game.lenses_time_left = 0;
3816   game.magnify_time_left = 0;
3817
3818   game.ball_active = level.ball_active_initial;
3819   game.ball_content_nr = 0;
3820
3821   game.explosions_delayed = TRUE;
3822
3823   game.envelope_active = FALSE;
3824
3825   for (i = 0; i < NUM_BELTS; i++)
3826   {
3827     game.belt_dir[i] = MV_NONE;
3828     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3829   }
3830
3831   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3832     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3833
3834 #if DEBUG_INIT_PLAYER
3835   DebugPrintPlayerStatus("Player status at level initialization");
3836 #endif
3837
3838   SCAN_PLAYFIELD(x, y)
3839   {
3840     Tile[x][y] = Last[x][y] = level.field[x][y];
3841     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3842     ChangeDelay[x][y] = 0;
3843     ChangePage[x][y] = -1;
3844     CustomValue[x][y] = 0;              // initialized in InitField()
3845     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3846     AmoebaNr[x][y] = 0;
3847     WasJustMoving[x][y] = 0;
3848     WasJustFalling[x][y] = 0;
3849     CheckCollision[x][y] = 0;
3850     CheckImpact[x][y] = 0;
3851     Stop[x][y] = FALSE;
3852     Pushed[x][y] = FALSE;
3853
3854     ChangeCount[x][y] = 0;
3855     ChangeEvent[x][y] = -1;
3856
3857     ExplodePhase[x][y] = 0;
3858     ExplodeDelay[x][y] = 0;
3859     ExplodeField[x][y] = EX_TYPE_NONE;
3860
3861     RunnerVisit[x][y] = 0;
3862     PlayerVisit[x][y] = 0;
3863
3864     GfxFrame[x][y] = 0;
3865     GfxRandom[x][y] = INIT_GFX_RANDOM();
3866     GfxElement[x][y] = EL_UNDEFINED;
3867     GfxAction[x][y] = ACTION_DEFAULT;
3868     GfxDir[x][y] = MV_NONE;
3869     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3870   }
3871
3872   SCAN_PLAYFIELD(x, y)
3873   {
3874     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3875       emulate_bd = FALSE;
3876     if (emulate_sb && !IS_SB_ELEMENT(Tile[x][y]))
3877       emulate_sb = FALSE;
3878     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3879       emulate_sp = FALSE;
3880
3881     InitField(x, y, TRUE);
3882
3883     ResetGfxAnimation(x, y);
3884   }
3885
3886   InitBeltMovement();
3887
3888   for (i = 0; i < MAX_PLAYERS; i++)
3889   {
3890     struct PlayerInfo *player = &stored_player[i];
3891
3892     // set number of special actions for bored and sleeping animation
3893     player->num_special_action_bored =
3894       get_num_special_action(player->artwork_element,
3895                              ACTION_BORING_1, ACTION_BORING_LAST);
3896     player->num_special_action_sleeping =
3897       get_num_special_action(player->artwork_element,
3898                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3899   }
3900
3901   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3902                     emulate_sb ? EMU_SOKOBAN :
3903                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3904
3905   // initialize type of slippery elements
3906   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3907   {
3908     if (!IS_CUSTOM_ELEMENT(i))
3909     {
3910       // default: elements slip down either to the left or right randomly
3911       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3912
3913       // SP style elements prefer to slip down on the left side
3914       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3915         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3916
3917       // BD style elements prefer to slip down on the left side
3918       if (game.emulation == EMU_BOULDERDASH)
3919         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3920     }
3921   }
3922
3923   // initialize explosion and ignition delay
3924   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3925   {
3926     if (!IS_CUSTOM_ELEMENT(i))
3927     {
3928       int num_phase = 8;
3929       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3930                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3931                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3932       int last_phase = (num_phase + 1) * delay;
3933       int half_phase = (num_phase / 2) * delay;
3934
3935       element_info[i].explosion_delay = last_phase - 1;
3936       element_info[i].ignition_delay = half_phase;
3937
3938       if (i == EL_BLACK_ORB)
3939         element_info[i].ignition_delay = 1;
3940     }
3941   }
3942
3943   // correct non-moving belts to start moving left
3944   for (i = 0; i < NUM_BELTS; i++)
3945     if (game.belt_dir[i] == MV_NONE)
3946       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3947
3948 #if USE_NEW_PLAYER_ASSIGNMENTS
3949   // use preferred player also in local single-player mode
3950   if (!network.enabled && !game.team_mode)
3951   {
3952     int new_index_nr = setup.network_player_nr;
3953
3954     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3955     {
3956       for (i = 0; i < MAX_PLAYERS; i++)
3957         stored_player[i].connected_locally = FALSE;
3958
3959       stored_player[new_index_nr].connected_locally = TRUE;
3960     }
3961   }
3962
3963   for (i = 0; i < MAX_PLAYERS; i++)
3964   {
3965     stored_player[i].connected = FALSE;
3966
3967     // in network game mode, the local player might not be the first player
3968     if (stored_player[i].connected_locally)
3969       local_player = &stored_player[i];
3970   }
3971
3972   if (!network.enabled)
3973     local_player->connected = TRUE;
3974
3975   if (tape.playing)
3976   {
3977     for (i = 0; i < MAX_PLAYERS; i++)
3978       stored_player[i].connected = tape.player_participates[i];
3979   }
3980   else if (network.enabled)
3981   {
3982     // add team mode players connected over the network (needed for correct
3983     // assignment of player figures from level to locally playing players)
3984
3985     for (i = 0; i < MAX_PLAYERS; i++)
3986       if (stored_player[i].connected_network)
3987         stored_player[i].connected = TRUE;
3988   }
3989   else if (game.team_mode)
3990   {
3991     // try to guess locally connected team mode players (needed for correct
3992     // assignment of player figures from level to locally playing players)
3993
3994     for (i = 0; i < MAX_PLAYERS; i++)
3995       if (setup.input[i].use_joystick ||
3996           setup.input[i].key.left != KSYM_UNDEFINED)
3997         stored_player[i].connected = TRUE;
3998   }
3999
4000 #if DEBUG_INIT_PLAYER
4001   DebugPrintPlayerStatus("Player status after level initialization");
4002 #endif
4003
4004 #if DEBUG_INIT_PLAYER
4005   Debug("game:init:player", "Reassigning players ...");
4006 #endif
4007
4008   // check if any connected player was not found in playfield
4009   for (i = 0; i < MAX_PLAYERS; i++)
4010   {
4011     struct PlayerInfo *player = &stored_player[i];
4012
4013     if (player->connected && !player->present)
4014     {
4015       struct PlayerInfo *field_player = NULL;
4016
4017 #if DEBUG_INIT_PLAYER
4018       Debug("game:init:player",
4019             "- looking for field player for player %d ...", i + 1);
4020 #endif
4021
4022       // assign first free player found that is present in the playfield
4023
4024       // first try: look for unmapped playfield player that is not connected
4025       for (j = 0; j < MAX_PLAYERS; j++)
4026         if (field_player == NULL &&
4027             stored_player[j].present &&
4028             !stored_player[j].mapped &&
4029             !stored_player[j].connected)
4030           field_player = &stored_player[j];
4031
4032       // second try: look for *any* unmapped playfield player
4033       for (j = 0; j < MAX_PLAYERS; j++)
4034         if (field_player == NULL &&
4035             stored_player[j].present &&
4036             !stored_player[j].mapped)
4037           field_player = &stored_player[j];
4038
4039       if (field_player != NULL)
4040       {
4041         int jx = field_player->jx, jy = field_player->jy;
4042
4043 #if DEBUG_INIT_PLAYER
4044         Debug("game:init:player", "- found player %d",
4045               field_player->index_nr + 1);
4046 #endif
4047
4048         player->present = FALSE;
4049         player->active = FALSE;
4050
4051         field_player->present = TRUE;
4052         field_player->active = TRUE;
4053
4054         /*
4055         player->initial_element = field_player->initial_element;
4056         player->artwork_element = field_player->artwork_element;
4057
4058         player->block_last_field       = field_player->block_last_field;
4059         player->block_delay_adjustment = field_player->block_delay_adjustment;
4060         */
4061
4062         StorePlayer[jx][jy] = field_player->element_nr;
4063
4064         field_player->jx = field_player->last_jx = jx;
4065         field_player->jy = field_player->last_jy = jy;
4066
4067         if (local_player == player)
4068           local_player = field_player;
4069
4070         map_player_action[field_player->index_nr] = i;
4071
4072         field_player->mapped = TRUE;
4073
4074 #if DEBUG_INIT_PLAYER
4075         Debug("game:init:player", "- map_player_action[%d] == %d",
4076               field_player->index_nr + 1, i + 1);
4077 #endif
4078       }
4079     }
4080
4081     if (player->connected && player->present)
4082       player->mapped = TRUE;
4083   }
4084
4085 #if DEBUG_INIT_PLAYER
4086   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4087 #endif
4088
4089 #else
4090
4091   // check if any connected player was not found in playfield
4092   for (i = 0; i < MAX_PLAYERS; i++)
4093   {
4094     struct PlayerInfo *player = &stored_player[i];
4095
4096     if (player->connected && !player->present)
4097     {
4098       for (j = 0; j < MAX_PLAYERS; j++)
4099       {
4100         struct PlayerInfo *field_player = &stored_player[j];
4101         int jx = field_player->jx, jy = field_player->jy;
4102
4103         // assign first free player found that is present in the playfield
4104         if (field_player->present && !field_player->connected)
4105         {
4106           player->present = TRUE;
4107           player->active = TRUE;
4108
4109           field_player->present = FALSE;
4110           field_player->active = FALSE;
4111
4112           player->initial_element = field_player->initial_element;
4113           player->artwork_element = field_player->artwork_element;
4114
4115           player->block_last_field       = field_player->block_last_field;
4116           player->block_delay_adjustment = field_player->block_delay_adjustment;
4117
4118           StorePlayer[jx][jy] = player->element_nr;
4119
4120           player->jx = player->last_jx = jx;
4121           player->jy = player->last_jy = jy;
4122
4123           break;
4124         }
4125       }
4126     }
4127   }
4128 #endif
4129
4130 #if 0
4131   Debug("game:init:player", "local_player->present == %d",
4132         local_player->present);
4133 #endif
4134
4135   // set focus to local player for network games, else to all players
4136   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4137   game.centered_player_nr_next = game.centered_player_nr;
4138   game.set_centered_player = FALSE;
4139   game.set_centered_player_wrap = FALSE;
4140
4141   if (network_playing && tape.recording)
4142   {
4143     // store client dependent player focus when recording network games
4144     tape.centered_player_nr_next = game.centered_player_nr_next;
4145     tape.set_centered_player = TRUE;
4146   }
4147
4148   if (tape.playing)
4149   {
4150     // when playing a tape, eliminate all players who do not participate
4151
4152 #if USE_NEW_PLAYER_ASSIGNMENTS
4153
4154     if (!game.team_mode)
4155     {
4156       for (i = 0; i < MAX_PLAYERS; i++)
4157       {
4158         if (stored_player[i].active &&
4159             !tape.player_participates[map_player_action[i]])
4160         {
4161           struct PlayerInfo *player = &stored_player[i];
4162           int jx = player->jx, jy = player->jy;
4163
4164 #if DEBUG_INIT_PLAYER
4165           Debug("game:init:player", "Removing player %d at (%d, %d)",
4166                 i + 1, jx, jy);
4167 #endif
4168
4169           player->active = FALSE;
4170           StorePlayer[jx][jy] = 0;
4171           Tile[jx][jy] = EL_EMPTY;
4172         }
4173       }
4174     }
4175
4176 #else
4177
4178     for (i = 0; i < MAX_PLAYERS; i++)
4179     {
4180       if (stored_player[i].active &&
4181           !tape.player_participates[i])
4182       {
4183         struct PlayerInfo *player = &stored_player[i];
4184         int jx = player->jx, jy = player->jy;
4185
4186         player->active = FALSE;
4187         StorePlayer[jx][jy] = 0;
4188         Tile[jx][jy] = EL_EMPTY;
4189       }
4190     }
4191 #endif
4192   }
4193   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4194   {
4195     // when in single player mode, eliminate all but the local player
4196
4197     for (i = 0; i < MAX_PLAYERS; i++)
4198     {
4199       struct PlayerInfo *player = &stored_player[i];
4200
4201       if (player->active && player != local_player)
4202       {
4203         int jx = player->jx, jy = player->jy;
4204
4205         player->active = FALSE;
4206         player->present = FALSE;
4207
4208         StorePlayer[jx][jy] = 0;
4209         Tile[jx][jy] = EL_EMPTY;
4210       }
4211     }
4212   }
4213
4214   for (i = 0; i < MAX_PLAYERS; i++)
4215     if (stored_player[i].active)
4216       game.players_still_needed++;
4217
4218   if (level.solved_by_one_player)
4219     game.players_still_needed = 1;
4220
4221   // when recording the game, store which players take part in the game
4222   if (tape.recording)
4223   {
4224 #if USE_NEW_PLAYER_ASSIGNMENTS
4225     for (i = 0; i < MAX_PLAYERS; i++)
4226       if (stored_player[i].connected)
4227         tape.player_participates[i] = TRUE;
4228 #else
4229     for (i = 0; i < MAX_PLAYERS; i++)
4230       if (stored_player[i].active)
4231         tape.player_participates[i] = TRUE;
4232 #endif
4233   }
4234
4235 #if DEBUG_INIT_PLAYER
4236   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4237 #endif
4238
4239   if (BorderElement == EL_EMPTY)
4240   {
4241     SBX_Left = 0;
4242     SBX_Right = lev_fieldx - SCR_FIELDX;
4243     SBY_Upper = 0;
4244     SBY_Lower = lev_fieldy - SCR_FIELDY;
4245   }
4246   else
4247   {
4248     SBX_Left = -1;
4249     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4250     SBY_Upper = -1;
4251     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4252   }
4253
4254   if (full_lev_fieldx <= SCR_FIELDX)
4255     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4256   if (full_lev_fieldy <= SCR_FIELDY)
4257     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4258
4259   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4260     SBX_Left--;
4261   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4262     SBY_Upper--;
4263
4264   // if local player not found, look for custom element that might create
4265   // the player (make some assumptions about the right custom element)
4266   if (!local_player->present)
4267   {
4268     int start_x = 0, start_y = 0;
4269     int found_rating = 0;
4270     int found_element = EL_UNDEFINED;
4271     int player_nr = local_player->index_nr;
4272
4273     SCAN_PLAYFIELD(x, y)
4274     {
4275       int element = Tile[x][y];
4276       int content;
4277       int xx, yy;
4278       boolean is_player;
4279
4280       if (level.use_start_element[player_nr] &&
4281           level.start_element[player_nr] == element &&
4282           found_rating < 4)
4283       {
4284         start_x = x;
4285         start_y = y;
4286
4287         found_rating = 4;
4288         found_element = element;
4289       }
4290
4291       if (!IS_CUSTOM_ELEMENT(element))
4292         continue;
4293
4294       if (CAN_CHANGE(element))
4295       {
4296         for (i = 0; i < element_info[element].num_change_pages; i++)
4297         {
4298           // check for player created from custom element as single target
4299           content = element_info[element].change_page[i].target_element;
4300           is_player = ELEM_IS_PLAYER(content);
4301
4302           if (is_player && (found_rating < 3 ||
4303                             (found_rating == 3 && element < found_element)))
4304           {
4305             start_x = x;
4306             start_y = y;
4307
4308             found_rating = 3;
4309             found_element = element;
4310           }
4311         }
4312       }
4313
4314       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4315       {
4316         // check for player created from custom element as explosion content
4317         content = element_info[element].content.e[xx][yy];
4318         is_player = ELEM_IS_PLAYER(content);
4319
4320         if (is_player && (found_rating < 2 ||
4321                           (found_rating == 2 && element < found_element)))
4322         {
4323           start_x = x + xx - 1;
4324           start_y = y + yy - 1;
4325
4326           found_rating = 2;
4327           found_element = element;
4328         }
4329
4330         if (!CAN_CHANGE(element))
4331           continue;
4332
4333         for (i = 0; i < element_info[element].num_change_pages; i++)
4334         {
4335           // check for player created from custom element as extended target
4336           content =
4337             element_info[element].change_page[i].target_content.e[xx][yy];
4338
4339           is_player = ELEM_IS_PLAYER(content);
4340
4341           if (is_player && (found_rating < 1 ||
4342                             (found_rating == 1 && element < found_element)))
4343           {
4344             start_x = x + xx - 1;
4345             start_y = y + yy - 1;
4346
4347             found_rating = 1;
4348             found_element = element;
4349           }
4350         }
4351       }
4352     }
4353
4354     scroll_x = SCROLL_POSITION_X(start_x);
4355     scroll_y = SCROLL_POSITION_Y(start_y);
4356   }
4357   else
4358   {
4359     scroll_x = SCROLL_POSITION_X(local_player->jx);
4360     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4361   }
4362
4363   // !!! FIX THIS (START) !!!
4364   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4365   {
4366     InitGameEngine_EM();
4367   }
4368   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4369   {
4370     InitGameEngine_SP();
4371   }
4372   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4373   {
4374     InitGameEngine_MM();
4375   }
4376   else
4377   {
4378     DrawLevel(REDRAW_FIELD);
4379     DrawAllPlayers();
4380
4381     // after drawing the level, correct some elements
4382     if (game.timegate_time_left == 0)
4383       CloseAllOpenTimegates();
4384   }
4385
4386   // blit playfield from scroll buffer to normal back buffer for fading in
4387   BlitScreenToBitmap(backbuffer);
4388   // !!! FIX THIS (END) !!!
4389
4390   DrawMaskedBorder(fade_mask);
4391
4392   FadeIn(fade_mask);
4393
4394 #if 1
4395   // full screen redraw is required at this point in the following cases:
4396   // - special editor door undrawn when game was started from level editor
4397   // - drawing area (playfield) was changed and has to be removed completely
4398   redraw_mask = REDRAW_ALL;
4399   BackToFront();
4400 #endif
4401
4402   if (!game.restart_level)
4403   {
4404     // copy default game door content to main double buffer
4405
4406     // !!! CHECK AGAIN !!!
4407     SetPanelBackground();
4408     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4409     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4410   }
4411
4412   SetPanelBackground();
4413   SetDrawBackgroundMask(REDRAW_DOOR_1);
4414
4415   UpdateAndDisplayGameControlValues();
4416
4417   if (!game.restart_level)
4418   {
4419     UnmapGameButtons();
4420     UnmapTapeButtons();
4421
4422     FreeGameButtons();
4423     CreateGameButtons();
4424
4425     MapGameButtons();
4426     MapTapeButtons();
4427
4428     // copy actual game door content to door double buffer for OpenDoor()
4429     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4430
4431     OpenDoor(DOOR_OPEN_ALL);
4432
4433     KeyboardAutoRepeatOffUnlessAutoplay();
4434
4435 #if DEBUG_INIT_PLAYER
4436     DebugPrintPlayerStatus("Player status (final)");
4437 #endif
4438   }
4439
4440   UnmapAllGadgets();
4441
4442   MapGameButtons();
4443   MapTapeButtons();
4444
4445   if (!game.restart_level && !tape.playing)
4446   {
4447     LevelStats_incPlayed(level_nr);
4448
4449     SaveLevelSetup_SeriesInfo();
4450   }
4451
4452   game.restart_level = FALSE;
4453   game.restart_game_message = NULL;
4454
4455   game.request_active = FALSE;
4456   game.request_active_or_moving = FALSE;
4457
4458   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4459     InitGameActions_MM();
4460
4461   SaveEngineSnapshotToListInitial();
4462
4463   if (!game.restart_level)
4464   {
4465     PlaySound(SND_GAME_STARTING);
4466
4467     if (setup.sound_music)
4468       PlayLevelMusic();
4469   }
4470 }
4471
4472 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4473                         int actual_player_x, int actual_player_y)
4474 {
4475   // this is used for non-R'n'D game engines to update certain engine values
4476
4477   // needed to determine if sounds are played within the visible screen area
4478   scroll_x = actual_scroll_x;
4479   scroll_y = actual_scroll_y;
4480
4481   // needed to get player position for "follow finger" playing input method
4482   local_player->jx = actual_player_x;
4483   local_player->jy = actual_player_y;
4484 }
4485
4486 void InitMovDir(int x, int y)
4487 {
4488   int i, element = Tile[x][y];
4489   static int xy[4][2] =
4490   {
4491     {  0, +1 },
4492     { +1,  0 },
4493     {  0, -1 },
4494     { -1,  0 }
4495   };
4496   static int direction[3][4] =
4497   {
4498     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4499     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4500     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4501   };
4502
4503   switch (element)
4504   {
4505     case EL_BUG_RIGHT:
4506     case EL_BUG_UP:
4507     case EL_BUG_LEFT:
4508     case EL_BUG_DOWN:
4509       Tile[x][y] = EL_BUG;
4510       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4511       break;
4512
4513     case EL_SPACESHIP_RIGHT:
4514     case EL_SPACESHIP_UP:
4515     case EL_SPACESHIP_LEFT:
4516     case EL_SPACESHIP_DOWN:
4517       Tile[x][y] = EL_SPACESHIP;
4518       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4519       break;
4520
4521     case EL_BD_BUTTERFLY_RIGHT:
4522     case EL_BD_BUTTERFLY_UP:
4523     case EL_BD_BUTTERFLY_LEFT:
4524     case EL_BD_BUTTERFLY_DOWN:
4525       Tile[x][y] = EL_BD_BUTTERFLY;
4526       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4527       break;
4528
4529     case EL_BD_FIREFLY_RIGHT:
4530     case EL_BD_FIREFLY_UP:
4531     case EL_BD_FIREFLY_LEFT:
4532     case EL_BD_FIREFLY_DOWN:
4533       Tile[x][y] = EL_BD_FIREFLY;
4534       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4535       break;
4536
4537     case EL_PACMAN_RIGHT:
4538     case EL_PACMAN_UP:
4539     case EL_PACMAN_LEFT:
4540     case EL_PACMAN_DOWN:
4541       Tile[x][y] = EL_PACMAN;
4542       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4543       break;
4544
4545     case EL_YAMYAM_LEFT:
4546     case EL_YAMYAM_RIGHT:
4547     case EL_YAMYAM_UP:
4548     case EL_YAMYAM_DOWN:
4549       Tile[x][y] = EL_YAMYAM;
4550       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4551       break;
4552
4553     case EL_SP_SNIKSNAK:
4554       MovDir[x][y] = MV_UP;
4555       break;
4556
4557     case EL_SP_ELECTRON:
4558       MovDir[x][y] = MV_LEFT;
4559       break;
4560
4561     case EL_MOLE_LEFT:
4562     case EL_MOLE_RIGHT:
4563     case EL_MOLE_UP:
4564     case EL_MOLE_DOWN:
4565       Tile[x][y] = EL_MOLE;
4566       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4567       break;
4568
4569     case EL_SPRING_LEFT:
4570     case EL_SPRING_RIGHT:
4571       Tile[x][y] = EL_SPRING;
4572       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4573       break;
4574
4575     default:
4576       if (IS_CUSTOM_ELEMENT(element))
4577       {
4578         struct ElementInfo *ei = &element_info[element];
4579         int move_direction_initial = ei->move_direction_initial;
4580         int move_pattern = ei->move_pattern;
4581
4582         if (move_direction_initial == MV_START_PREVIOUS)
4583         {
4584           if (MovDir[x][y] != MV_NONE)
4585             return;
4586
4587           move_direction_initial = MV_START_AUTOMATIC;
4588         }
4589
4590         if (move_direction_initial == MV_START_RANDOM)
4591           MovDir[x][y] = 1 << RND(4);
4592         else if (move_direction_initial & MV_ANY_DIRECTION)
4593           MovDir[x][y] = move_direction_initial;
4594         else if (move_pattern == MV_ALL_DIRECTIONS ||
4595                  move_pattern == MV_TURNING_LEFT ||
4596                  move_pattern == MV_TURNING_RIGHT ||
4597                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4598                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4599                  move_pattern == MV_TURNING_RANDOM)
4600           MovDir[x][y] = 1 << RND(4);
4601         else if (move_pattern == MV_HORIZONTAL)
4602           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4603         else if (move_pattern == MV_VERTICAL)
4604           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4605         else if (move_pattern & MV_ANY_DIRECTION)
4606           MovDir[x][y] = element_info[element].move_pattern;
4607         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4608                  move_pattern == MV_ALONG_RIGHT_SIDE)
4609         {
4610           // use random direction as default start direction
4611           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4612             MovDir[x][y] = 1 << RND(4);
4613
4614           for (i = 0; i < NUM_DIRECTIONS; i++)
4615           {
4616             int x1 = x + xy[i][0];
4617             int y1 = y + xy[i][1];
4618
4619             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4620             {
4621               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4622                 MovDir[x][y] = direction[0][i];
4623               else
4624                 MovDir[x][y] = direction[1][i];
4625
4626               break;
4627             }
4628           }
4629         }                
4630       }
4631       else
4632       {
4633         MovDir[x][y] = 1 << RND(4);
4634
4635         if (element != EL_BUG &&
4636             element != EL_SPACESHIP &&
4637             element != EL_BD_BUTTERFLY &&
4638             element != EL_BD_FIREFLY)
4639           break;
4640
4641         for (i = 0; i < NUM_DIRECTIONS; i++)
4642         {
4643           int x1 = x + xy[i][0];
4644           int y1 = y + xy[i][1];
4645
4646           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4647           {
4648             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4649             {
4650               MovDir[x][y] = direction[0][i];
4651               break;
4652             }
4653             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4654                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4655             {
4656               MovDir[x][y] = direction[1][i];
4657               break;
4658             }
4659           }
4660         }
4661       }
4662       break;
4663   }
4664
4665   GfxDir[x][y] = MovDir[x][y];
4666 }
4667
4668 void InitAmoebaNr(int x, int y)
4669 {
4670   int i;
4671   int group_nr = AmoebaNeighbourNr(x, y);
4672
4673   if (group_nr == 0)
4674   {
4675     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4676     {
4677       if (AmoebaCnt[i] == 0)
4678       {
4679         group_nr = i;
4680         break;
4681       }
4682     }
4683   }
4684
4685   AmoebaNr[x][y] = group_nr;
4686   AmoebaCnt[group_nr]++;
4687   AmoebaCnt2[group_nr]++;
4688 }
4689
4690 static void LevelSolved(void)
4691 {
4692   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4693       game.players_still_needed > 0)
4694     return;
4695
4696   game.LevelSolved = TRUE;
4697   game.GameOver = TRUE;
4698
4699   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4700                       game_em.lev->score :
4701                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4702                       game_mm.score :
4703                       game.score);
4704   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4705                        MM_HEALTH(game_mm.laser_overload_value) :
4706                        game.health);
4707
4708   game.LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed : TimeLeft);
4709   game.LevelSolved_CountingScore = game.score_final;
4710   game.LevelSolved_CountingHealth = game.health_final;
4711 }
4712
4713 void GameWon(void)
4714 {
4715   static int time_count_steps;
4716   static int time, time_final;
4717   static int score, score_final;
4718   static int health, health_final;
4719   static int game_over_delay_1 = 0;
4720   static int game_over_delay_2 = 0;
4721   static int game_over_delay_3 = 0;
4722   int game_over_delay_value_1 = 50;
4723   int game_over_delay_value_2 = 25;
4724   int game_over_delay_value_3 = 50;
4725
4726   if (!game.LevelSolved_GameWon)
4727   {
4728     int i;
4729
4730     // do not start end game actions before the player stops moving (to exit)
4731     if (local_player->active && local_player->MovPos)
4732       return;
4733
4734     game.LevelSolved_GameWon = TRUE;
4735     game.LevelSolved_SaveTape = tape.recording;
4736     game.LevelSolved_SaveScore = !tape.playing;
4737
4738     if (!tape.playing)
4739     {
4740       LevelStats_incSolved(level_nr);
4741
4742       SaveLevelSetup_SeriesInfo();
4743     }
4744
4745     if (tape.auto_play)         // tape might already be stopped here
4746       tape.auto_play_level_solved = TRUE;
4747
4748     TapeStop();
4749
4750     game_over_delay_1 = 0;
4751     game_over_delay_2 = 0;
4752     game_over_delay_3 = game_over_delay_value_3;
4753
4754     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4755     score = score_final = game.score_final;
4756     health = health_final = game.health_final;
4757
4758     if (level.score[SC_TIME_BONUS] > 0)
4759     {
4760       if (TimeLeft > 0)
4761       {
4762         time_final = 0;
4763         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4764       }
4765       else if (game.no_time_limit && TimePlayed < 999)
4766       {
4767         time_final = 999;
4768         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4769       }
4770
4771       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4772
4773       game_over_delay_1 = game_over_delay_value_1;
4774
4775       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4776       {
4777         health_final = 0;
4778         score_final += health * level.score[SC_TIME_BONUS];
4779
4780         game_over_delay_2 = game_over_delay_value_2;
4781       }
4782
4783       game.score_final = score_final;
4784       game.health_final = health_final;
4785     }
4786
4787     if (level_editor_test_game)
4788     {
4789       time = time_final;
4790       score = score_final;
4791
4792       game.LevelSolved_CountingTime = time;
4793       game.LevelSolved_CountingScore = score;
4794
4795       game_panel_controls[GAME_PANEL_TIME].value = time;
4796       game_panel_controls[GAME_PANEL_SCORE].value = score;
4797
4798       DisplayGameControlValues();
4799     }
4800
4801     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4802     {
4803       // check if last player has left the level
4804       if (game.exit_x >= 0 &&
4805           game.exit_y >= 0)
4806       {
4807         int x = game.exit_x;
4808         int y = game.exit_y;
4809         int element = Tile[x][y];
4810
4811         // close exit door after last player
4812         if ((game.all_players_gone &&
4813              (element == EL_EXIT_OPEN ||
4814               element == EL_SP_EXIT_OPEN ||
4815               element == EL_STEEL_EXIT_OPEN)) ||
4816             element == EL_EM_EXIT_OPEN ||
4817             element == EL_EM_STEEL_EXIT_OPEN)
4818         {
4819
4820           Tile[x][y] =
4821             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4822              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4823              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4824              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4825              EL_EM_STEEL_EXIT_CLOSING);
4826
4827           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4828         }
4829
4830         // player disappears
4831         DrawLevelField(x, y);
4832       }
4833
4834       for (i = 0; i < MAX_PLAYERS; i++)
4835       {
4836         struct PlayerInfo *player = &stored_player[i];
4837
4838         if (player->present)
4839         {
4840           RemovePlayer(player);
4841
4842           // player disappears
4843           DrawLevelField(player->jx, player->jy);
4844         }
4845       }
4846     }
4847
4848     PlaySound(SND_GAME_WINNING);
4849   }
4850
4851   if (game_over_delay_1 > 0)
4852   {
4853     game_over_delay_1--;
4854
4855     return;
4856   }
4857
4858   if (time != time_final)
4859   {
4860     int time_to_go = ABS(time_final - time);
4861     int time_count_dir = (time < time_final ? +1 : -1);
4862
4863     if (time_to_go < time_count_steps)
4864       time_count_steps = 1;
4865
4866     time  += time_count_steps * time_count_dir;
4867     score += time_count_steps * level.score[SC_TIME_BONUS];
4868
4869     game.LevelSolved_CountingTime = time;
4870     game.LevelSolved_CountingScore = score;
4871
4872     game_panel_controls[GAME_PANEL_TIME].value = time;
4873     game_panel_controls[GAME_PANEL_SCORE].value = score;
4874
4875     DisplayGameControlValues();
4876
4877     if (time == time_final)
4878       StopSound(SND_GAME_LEVELTIME_BONUS);
4879     else if (setup.sound_loops)
4880       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4881     else
4882       PlaySound(SND_GAME_LEVELTIME_BONUS);
4883
4884     return;
4885   }
4886
4887   if (game_over_delay_2 > 0)
4888   {
4889     game_over_delay_2--;
4890
4891     return;
4892   }
4893
4894   if (health != health_final)
4895   {
4896     int health_count_dir = (health < health_final ? +1 : -1);
4897
4898     health += health_count_dir;
4899     score  += level.score[SC_TIME_BONUS];
4900
4901     game.LevelSolved_CountingHealth = health;
4902     game.LevelSolved_CountingScore = score;
4903
4904     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4905     game_panel_controls[GAME_PANEL_SCORE].value = score;
4906
4907     DisplayGameControlValues();
4908
4909     if (health == health_final)
4910       StopSound(SND_GAME_LEVELTIME_BONUS);
4911     else if (setup.sound_loops)
4912       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4913     else
4914       PlaySound(SND_GAME_LEVELTIME_BONUS);
4915
4916     return;
4917   }
4918
4919   game.panel.active = FALSE;
4920
4921   if (game_over_delay_3 > 0)
4922   {
4923     game_over_delay_3--;
4924
4925     return;
4926   }
4927
4928   GameEnd();
4929 }
4930
4931 void GameEnd(void)
4932 {
4933   // used instead of "level_nr" (needed for network games)
4934   int last_level_nr = levelset.level_nr;
4935   int hi_pos;
4936
4937   game.LevelSolved_GameEnd = TRUE;
4938
4939   if (game.LevelSolved_SaveTape)
4940   {
4941     // make sure that request dialog to save tape does not open door again
4942     if (!global.use_envelope_request)
4943       CloseDoor(DOOR_CLOSE_1);
4944
4945     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4946   }
4947
4948   // if no tape is to be saved, close both doors simultaneously
4949   CloseDoor(DOOR_CLOSE_ALL);
4950
4951   if (level_editor_test_game)
4952   {
4953     SetGameStatus(GAME_MODE_MAIN);
4954
4955     DrawMainMenu();
4956
4957     return;
4958   }
4959
4960   if (!game.LevelSolved_SaveScore)
4961   {
4962     SetGameStatus(GAME_MODE_MAIN);
4963
4964     DrawMainMenu();
4965
4966     return;
4967   }
4968
4969   if (level_nr == leveldir_current->handicap_level)
4970   {
4971     leveldir_current->handicap_level++;
4972
4973     SaveLevelSetup_SeriesInfo();
4974   }
4975
4976   if (setup.increment_levels &&
4977       level_nr < leveldir_current->last_level &&
4978       !network_playing)
4979   {
4980     level_nr++;         // advance to next level
4981     TapeErase();        // start with empty tape
4982
4983     if (setup.auto_play_next_level)
4984     {
4985       LoadLevel(level_nr);
4986
4987       SaveLevelSetup_SeriesInfo();
4988     }
4989   }
4990
4991   hi_pos = NewHiScore(last_level_nr);
4992
4993   if (hi_pos >= 0 && !setup.skip_scores_after_game)
4994   {
4995     SetGameStatus(GAME_MODE_SCORES);
4996
4997     DrawHallOfFame(last_level_nr, hi_pos);
4998   }
4999   else if (setup.auto_play_next_level && setup.increment_levels &&
5000            last_level_nr < leveldir_current->last_level &&
5001            !network_playing)
5002   {
5003     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5004   }
5005   else
5006   {
5007     SetGameStatus(GAME_MODE_MAIN);
5008
5009     DrawMainMenu();
5010   }
5011 }
5012
5013 int NewHiScore(int level_nr)
5014 {
5015   int k, l;
5016   int position = -1;
5017   boolean one_score_entry_per_name = !program.many_scores_per_name;
5018
5019   LoadScore(level_nr);
5020
5021   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5022       game.score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
5023     return -1;
5024
5025   for (k = 0; k < MAX_SCORE_ENTRIES; k++)
5026   {
5027     if (game.score_final > highscore[k].Score)
5028     {
5029       // player has made it to the hall of fame
5030
5031       if (k < MAX_SCORE_ENTRIES - 1)
5032       {
5033         int m = MAX_SCORE_ENTRIES - 1;
5034
5035         if (one_score_entry_per_name)
5036         {
5037           for (l = k; l < MAX_SCORE_ENTRIES; l++)
5038             if (strEqual(setup.player_name, highscore[l].Name))
5039               m = l;
5040
5041           if (m == k)   // player's new highscore overwrites his old one
5042             goto put_into_list;
5043         }
5044
5045         for (l = m; l > k; l--)
5046         {
5047           strcpy(highscore[l].Name, highscore[l - 1].Name);
5048           highscore[l].Score = highscore[l - 1].Score;
5049         }
5050       }
5051
5052       put_into_list:
5053
5054       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5055       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5056       highscore[k].Score = game.score_final;
5057       position = k;
5058
5059       break;
5060     }
5061     else if (one_score_entry_per_name &&
5062              !strncmp(setup.player_name, highscore[k].Name,
5063                       MAX_PLAYER_NAME_LEN))
5064       break;    // player already there with a higher score
5065   }
5066
5067   if (position >= 0) 
5068     SaveScore(level_nr);
5069
5070   return position;
5071 }
5072
5073 static int getElementMoveStepsizeExt(int x, int y, int direction)
5074 {
5075   int element = Tile[x][y];
5076   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5077   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5078   int horiz_move = (dx != 0);
5079   int sign = (horiz_move ? dx : dy);
5080   int step = sign * element_info[element].move_stepsize;
5081
5082   // special values for move stepsize for spring and things on conveyor belt
5083   if (horiz_move)
5084   {
5085     if (CAN_FALL(element) &&
5086         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5087       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5088     else if (element == EL_SPRING)
5089       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5090   }
5091
5092   return step;
5093 }
5094
5095 static int getElementMoveStepsize(int x, int y)
5096 {
5097   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5098 }
5099
5100 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5101 {
5102   if (player->GfxAction != action || player->GfxDir != dir)
5103   {
5104     player->GfxAction = action;
5105     player->GfxDir = dir;
5106     player->Frame = 0;
5107     player->StepFrame = 0;
5108   }
5109 }
5110
5111 static void ResetGfxFrame(int x, int y)
5112 {
5113   // profiling showed that "autotest" spends 10~20% of its time in this function
5114   if (DrawingDeactivatedField())
5115     return;
5116
5117   int element = Tile[x][y];
5118   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5119
5120   if (graphic_info[graphic].anim_global_sync)
5121     GfxFrame[x][y] = FrameCounter;
5122   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5123     GfxFrame[x][y] = CustomValue[x][y];
5124   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5125     GfxFrame[x][y] = element_info[element].collect_score;
5126   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5127     GfxFrame[x][y] = ChangeDelay[x][y];
5128 }
5129
5130 static void ResetGfxAnimation(int x, int y)
5131 {
5132   GfxAction[x][y] = ACTION_DEFAULT;
5133   GfxDir[x][y] = MovDir[x][y];
5134   GfxFrame[x][y] = 0;
5135
5136   ResetGfxFrame(x, y);
5137 }
5138
5139 static void ResetRandomAnimationValue(int x, int y)
5140 {
5141   GfxRandom[x][y] = INIT_GFX_RANDOM();
5142 }
5143
5144 static void InitMovingField(int x, int y, int direction)
5145 {
5146   int element = Tile[x][y];
5147   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5148   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5149   int newx = x + dx;
5150   int newy = y + dy;
5151   boolean is_moving_before, is_moving_after;
5152
5153   // check if element was/is moving or being moved before/after mode change
5154   is_moving_before = (WasJustMoving[x][y] != 0);
5155   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5156
5157   // reset animation only for moving elements which change direction of moving
5158   // or which just started or stopped moving
5159   // (else CEs with property "can move" / "not moving" are reset each frame)
5160   if (is_moving_before != is_moving_after ||
5161       direction != MovDir[x][y])
5162     ResetGfxAnimation(x, y);
5163
5164   MovDir[x][y] = direction;
5165   GfxDir[x][y] = direction;
5166
5167   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5168                      direction == MV_DOWN && CAN_FALL(element) ?
5169                      ACTION_FALLING : ACTION_MOVING);
5170
5171   // this is needed for CEs with property "can move" / "not moving"
5172
5173   if (is_moving_after)
5174   {
5175     if (Tile[newx][newy] == EL_EMPTY)
5176       Tile[newx][newy] = EL_BLOCKED;
5177
5178     MovDir[newx][newy] = MovDir[x][y];
5179
5180     CustomValue[newx][newy] = CustomValue[x][y];
5181
5182     GfxFrame[newx][newy] = GfxFrame[x][y];
5183     GfxRandom[newx][newy] = GfxRandom[x][y];
5184     GfxAction[newx][newy] = GfxAction[x][y];
5185     GfxDir[newx][newy] = GfxDir[x][y];
5186   }
5187 }
5188
5189 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5190 {
5191   int direction = MovDir[x][y];
5192   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5193   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5194
5195   *goes_to_x = newx;
5196   *goes_to_y = newy;
5197 }
5198
5199 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5200 {
5201   int oldx = x, oldy = y;
5202   int direction = MovDir[x][y];
5203
5204   if (direction == MV_LEFT)
5205     oldx++;
5206   else if (direction == MV_RIGHT)
5207     oldx--;
5208   else if (direction == MV_UP)
5209     oldy++;
5210   else if (direction == MV_DOWN)
5211     oldy--;
5212
5213   *comes_from_x = oldx;
5214   *comes_from_y = oldy;
5215 }
5216
5217 static int MovingOrBlocked2Element(int x, int y)
5218 {
5219   int element = Tile[x][y];
5220
5221   if (element == EL_BLOCKED)
5222   {
5223     int oldx, oldy;
5224
5225     Blocked2Moving(x, y, &oldx, &oldy);
5226     return Tile[oldx][oldy];
5227   }
5228   else
5229     return element;
5230 }
5231
5232 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5233 {
5234   // like MovingOrBlocked2Element(), but if element is moving
5235   // and (x,y) is the field the moving element is just leaving,
5236   // return EL_BLOCKED instead of the element value
5237   int element = Tile[x][y];
5238
5239   if (IS_MOVING(x, y))
5240   {
5241     if (element == EL_BLOCKED)
5242     {
5243       int oldx, oldy;
5244
5245       Blocked2Moving(x, y, &oldx, &oldy);
5246       return Tile[oldx][oldy];
5247     }
5248     else
5249       return EL_BLOCKED;
5250   }
5251   else
5252     return element;
5253 }
5254
5255 static void RemoveField(int x, int y)
5256 {
5257   Tile[x][y] = EL_EMPTY;
5258
5259   MovPos[x][y] = 0;
5260   MovDir[x][y] = 0;
5261   MovDelay[x][y] = 0;
5262
5263   CustomValue[x][y] = 0;
5264
5265   AmoebaNr[x][y] = 0;
5266   ChangeDelay[x][y] = 0;
5267   ChangePage[x][y] = -1;
5268   Pushed[x][y] = FALSE;
5269
5270   GfxElement[x][y] = EL_UNDEFINED;
5271   GfxAction[x][y] = ACTION_DEFAULT;
5272   GfxDir[x][y] = MV_NONE;
5273 }
5274
5275 static void RemoveMovingField(int x, int y)
5276 {
5277   int oldx = x, oldy = y, newx = x, newy = y;
5278   int element = Tile[x][y];
5279   int next_element = EL_UNDEFINED;
5280
5281   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5282     return;
5283
5284   if (IS_MOVING(x, y))
5285   {
5286     Moving2Blocked(x, y, &newx, &newy);
5287
5288     if (Tile[newx][newy] != EL_BLOCKED)
5289     {
5290       // element is moving, but target field is not free (blocked), but
5291       // already occupied by something different (example: acid pool);
5292       // in this case, only remove the moving field, but not the target
5293
5294       RemoveField(oldx, oldy);
5295
5296       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5297
5298       TEST_DrawLevelField(oldx, oldy);
5299
5300       return;
5301     }
5302   }
5303   else if (element == EL_BLOCKED)
5304   {
5305     Blocked2Moving(x, y, &oldx, &oldy);
5306     if (!IS_MOVING(oldx, oldy))
5307       return;
5308   }
5309
5310   if (element == EL_BLOCKED &&
5311       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5312        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5313        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5314        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5315        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5316        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5317     next_element = get_next_element(Tile[oldx][oldy]);
5318
5319   RemoveField(oldx, oldy);
5320   RemoveField(newx, newy);
5321
5322   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5323
5324   if (next_element != EL_UNDEFINED)
5325     Tile[oldx][oldy] = next_element;
5326
5327   TEST_DrawLevelField(oldx, oldy);
5328   TEST_DrawLevelField(newx, newy);
5329 }
5330
5331 void DrawDynamite(int x, int y)
5332 {
5333   int sx = SCREENX(x), sy = SCREENY(y);
5334   int graphic = el2img(Tile[x][y]);
5335   int frame;
5336
5337   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5338     return;
5339
5340   if (IS_WALKABLE_INSIDE(Back[x][y]))
5341     return;
5342
5343   if (Back[x][y])
5344     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5345   else if (Store[x][y])
5346     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5347
5348   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5349
5350   if (Back[x][y] || Store[x][y])
5351     DrawGraphicThruMask(sx, sy, graphic, frame);
5352   else
5353     DrawGraphic(sx, sy, graphic, frame);
5354 }
5355
5356 static void CheckDynamite(int x, int y)
5357 {
5358   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5359   {
5360     MovDelay[x][y]--;
5361
5362     if (MovDelay[x][y] != 0)
5363     {
5364       DrawDynamite(x, y);
5365       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5366
5367       return;
5368     }
5369   }
5370
5371   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5372
5373   Bang(x, y);
5374 }
5375
5376 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5377 {
5378   boolean num_checked_players = 0;
5379   int i;
5380
5381   for (i = 0; i < MAX_PLAYERS; i++)
5382   {
5383     if (stored_player[i].active)
5384     {
5385       int sx = stored_player[i].jx;
5386       int sy = stored_player[i].jy;
5387
5388       if (num_checked_players == 0)
5389       {
5390         *sx1 = *sx2 = sx;
5391         *sy1 = *sy2 = sy;
5392       }
5393       else
5394       {
5395         *sx1 = MIN(*sx1, sx);
5396         *sy1 = MIN(*sy1, sy);
5397         *sx2 = MAX(*sx2, sx);
5398         *sy2 = MAX(*sy2, sy);
5399       }
5400
5401       num_checked_players++;
5402     }
5403   }
5404 }
5405
5406 static boolean checkIfAllPlayersFitToScreen_RND(void)
5407 {
5408   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5409
5410   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5411
5412   return (sx2 - sx1 < SCR_FIELDX &&
5413           sy2 - sy1 < SCR_FIELDY);
5414 }
5415
5416 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5417 {
5418   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5419
5420   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5421
5422   *sx = (sx1 + sx2) / 2;
5423   *sy = (sy1 + sy2) / 2;
5424 }
5425
5426 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5427                                boolean center_screen, boolean quick_relocation)
5428 {
5429   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5430   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5431   boolean no_delay = (tape.warp_forward);
5432   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5433   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5434   int new_scroll_x, new_scroll_y;
5435
5436   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5437   {
5438     // case 1: quick relocation inside visible screen (without scrolling)
5439
5440     RedrawPlayfield();
5441
5442     return;
5443   }
5444
5445   if (!level.shifted_relocation || center_screen)
5446   {
5447     // relocation _with_ centering of screen
5448
5449     new_scroll_x = SCROLL_POSITION_X(x);
5450     new_scroll_y = SCROLL_POSITION_Y(y);
5451   }
5452   else
5453   {
5454     // relocation _without_ centering of screen
5455
5456     int center_scroll_x = SCROLL_POSITION_X(old_x);
5457     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5458     int offset_x = x + (scroll_x - center_scroll_x);
5459     int offset_y = y + (scroll_y - center_scroll_y);
5460
5461     // for new screen position, apply previous offset to center position
5462     new_scroll_x = SCROLL_POSITION_X(offset_x);
5463     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5464   }
5465
5466   if (quick_relocation)
5467   {
5468     // case 2: quick relocation (redraw without visible scrolling)
5469
5470     scroll_x = new_scroll_x;
5471     scroll_y = new_scroll_y;
5472
5473     RedrawPlayfield();
5474
5475     return;
5476   }
5477
5478   // case 3: visible relocation (with scrolling to new position)
5479
5480   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5481
5482   SetVideoFrameDelay(wait_delay_value);
5483
5484   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5485   {
5486     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5487     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5488
5489     if (dx == 0 && dy == 0)             // no scrolling needed at all
5490       break;
5491
5492     scroll_x -= dx;
5493     scroll_y -= dy;
5494
5495     // set values for horizontal/vertical screen scrolling (half tile size)
5496     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5497     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5498     int pos_x = dx * TILEX / 2;
5499     int pos_y = dy * TILEY / 2;
5500     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5501     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5502
5503     ScrollLevel(dx, dy);
5504     DrawAllPlayers();
5505
5506     // scroll in two steps of half tile size to make things smoother
5507     BlitScreenToBitmapExt_RND(window, fx, fy);
5508
5509     // scroll second step to align at full tile size
5510     BlitScreenToBitmap(window);
5511   }
5512
5513   DrawAllPlayers();
5514   BackToFront();
5515
5516   SetVideoFrameDelay(frame_delay_value_old);
5517 }
5518
5519 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5520 {
5521   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5522   int player_nr = GET_PLAYER_NR(el_player);
5523   struct PlayerInfo *player = &stored_player[player_nr];
5524   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5525   boolean no_delay = (tape.warp_forward);
5526   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5527   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5528   int old_jx = player->jx;
5529   int old_jy = player->jy;
5530   int old_element = Tile[old_jx][old_jy];
5531   int element = Tile[jx][jy];
5532   boolean player_relocated = (old_jx != jx || old_jy != jy);
5533
5534   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5535   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5536   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5537   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5538   int leave_side_horiz = move_dir_horiz;
5539   int leave_side_vert  = move_dir_vert;
5540   int enter_side = enter_side_horiz | enter_side_vert;
5541   int leave_side = leave_side_horiz | leave_side_vert;
5542
5543   if (player->buried)           // do not reanimate dead player
5544     return;
5545
5546   if (!player_relocated)        // no need to relocate the player
5547     return;
5548
5549   if (IS_PLAYER(jx, jy))        // player already placed at new position
5550   {
5551     RemoveField(jx, jy);        // temporarily remove newly placed player
5552     DrawLevelField(jx, jy);
5553   }
5554
5555   if (player->present)
5556   {
5557     while (player->MovPos)
5558     {
5559       ScrollPlayer(player, SCROLL_GO_ON);
5560       ScrollScreen(NULL, SCROLL_GO_ON);
5561
5562       AdvanceFrameAndPlayerCounters(player->index_nr);
5563
5564       DrawPlayer(player);
5565
5566       BackToFront_WithFrameDelay(wait_delay_value);
5567     }
5568
5569     DrawPlayer(player);         // needed here only to cleanup last field
5570     DrawLevelField(player->jx, player->jy);     // remove player graphic
5571
5572     player->is_moving = FALSE;
5573   }
5574
5575   if (IS_CUSTOM_ELEMENT(old_element))
5576     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5577                                CE_LEFT_BY_PLAYER,
5578                                player->index_bit, leave_side);
5579
5580   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5581                                       CE_PLAYER_LEAVES_X,
5582                                       player->index_bit, leave_side);
5583
5584   Tile[jx][jy] = el_player;
5585   InitPlayerField(jx, jy, el_player, TRUE);
5586
5587   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5588      possible that the relocation target field did not contain a player element,
5589      but a walkable element, to which the new player was relocated -- in this
5590      case, restore that (already initialized!) element on the player field */
5591   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5592   {
5593     Tile[jx][jy] = element;     // restore previously existing element
5594   }
5595
5596   // only visually relocate centered player
5597   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5598                      FALSE, level.instant_relocation);
5599
5600   TestIfPlayerTouchesBadThing(jx, jy);
5601   TestIfPlayerTouchesCustomElement(jx, jy);
5602
5603   if (IS_CUSTOM_ELEMENT(element))
5604     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5605                                player->index_bit, enter_side);
5606
5607   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5608                                       player->index_bit, enter_side);
5609
5610   if (player->is_switching)
5611   {
5612     /* ensure that relocation while still switching an element does not cause
5613        a new element to be treated as also switched directly after relocation
5614        (this is important for teleporter switches that teleport the player to
5615        a place where another teleporter switch is in the same direction, which
5616        would then incorrectly be treated as immediately switched before the
5617        direction key that caused the switch was released) */
5618
5619     player->switch_x += jx - old_jx;
5620     player->switch_y += jy - old_jy;
5621   }
5622 }
5623
5624 static void Explode(int ex, int ey, int phase, int mode)
5625 {
5626   int x, y;
5627   int last_phase;
5628   int border_element;
5629
5630   // !!! eliminate this variable !!!
5631   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5632
5633   if (game.explosions_delayed)
5634   {
5635     ExplodeField[ex][ey] = mode;
5636     return;
5637   }
5638
5639   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5640   {
5641     int center_element = Tile[ex][ey];
5642     int artwork_element, explosion_element;     // set these values later
5643
5644     // remove things displayed in background while burning dynamite
5645     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5646       Back[ex][ey] = 0;
5647
5648     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5649     {
5650       // put moving element to center field (and let it explode there)
5651       center_element = MovingOrBlocked2Element(ex, ey);
5652       RemoveMovingField(ex, ey);
5653       Tile[ex][ey] = center_element;
5654     }
5655
5656     // now "center_element" is finally determined -- set related values now
5657     artwork_element = center_element;           // for custom player artwork
5658     explosion_element = center_element;         // for custom player artwork
5659
5660     if (IS_PLAYER(ex, ey))
5661     {
5662       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5663
5664       artwork_element = stored_player[player_nr].artwork_element;
5665
5666       if (level.use_explosion_element[player_nr])
5667       {
5668         explosion_element = level.explosion_element[player_nr];
5669         artwork_element = explosion_element;
5670       }
5671     }
5672
5673     if (mode == EX_TYPE_NORMAL ||
5674         mode == EX_TYPE_CENTER ||
5675         mode == EX_TYPE_CROSS)
5676       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5677
5678     last_phase = element_info[explosion_element].explosion_delay + 1;
5679
5680     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5681     {
5682       int xx = x - ex + 1;
5683       int yy = y - ey + 1;
5684       int element;
5685
5686       if (!IN_LEV_FIELD(x, y) ||
5687           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5688           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5689         continue;
5690
5691       element = Tile[x][y];
5692
5693       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5694       {
5695         element = MovingOrBlocked2Element(x, y);
5696
5697         if (!IS_EXPLOSION_PROOF(element))
5698           RemoveMovingField(x, y);
5699       }
5700
5701       // indestructible elements can only explode in center (but not flames)
5702       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5703                                            mode == EX_TYPE_BORDER)) ||
5704           element == EL_FLAMES)
5705         continue;
5706
5707       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5708          behaviour, for example when touching a yamyam that explodes to rocks
5709          with active deadly shield, a rock is created under the player !!! */
5710       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5711 #if 0
5712       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5713           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5714            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5715 #else
5716       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5717 #endif
5718       {
5719         if (IS_ACTIVE_BOMB(element))
5720         {
5721           // re-activate things under the bomb like gate or penguin
5722           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5723           Back[x][y] = 0;
5724         }
5725
5726         continue;
5727       }
5728
5729       // save walkable background elements while explosion on same tile
5730       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5731           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5732         Back[x][y] = element;
5733
5734       // ignite explodable elements reached by other explosion
5735       if (element == EL_EXPLOSION)
5736         element = Store2[x][y];
5737
5738       if (AmoebaNr[x][y] &&
5739           (element == EL_AMOEBA_FULL ||
5740            element == EL_BD_AMOEBA ||
5741            element == EL_AMOEBA_GROWING))
5742       {
5743         AmoebaCnt[AmoebaNr[x][y]]--;
5744         AmoebaCnt2[AmoebaNr[x][y]]--;
5745       }
5746
5747       RemoveField(x, y);
5748
5749       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5750       {
5751         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5752
5753         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5754
5755         if (PLAYERINFO(ex, ey)->use_murphy)
5756           Store[x][y] = EL_EMPTY;
5757       }
5758
5759       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5760       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5761       else if (ELEM_IS_PLAYER(center_element))
5762         Store[x][y] = EL_EMPTY;
5763       else if (center_element == EL_YAMYAM)
5764         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5765       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5766         Store[x][y] = element_info[center_element].content.e[xx][yy];
5767 #if 1
5768       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5769       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5770       // otherwise) -- FIX THIS !!!
5771       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5772         Store[x][y] = element_info[element].content.e[1][1];
5773 #else
5774       else if (!CAN_EXPLODE(element))
5775         Store[x][y] = element_info[element].content.e[1][1];
5776 #endif
5777       else
5778         Store[x][y] = EL_EMPTY;
5779
5780       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5781           center_element == EL_AMOEBA_TO_DIAMOND)
5782         Store2[x][y] = element;
5783
5784       Tile[x][y] = EL_EXPLOSION;
5785       GfxElement[x][y] = artwork_element;
5786
5787       ExplodePhase[x][y] = 1;
5788       ExplodeDelay[x][y] = last_phase;
5789
5790       Stop[x][y] = TRUE;
5791     }
5792
5793     if (center_element == EL_YAMYAM)
5794       game.yamyam_content_nr =
5795         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5796
5797     return;
5798   }
5799
5800   if (Stop[ex][ey])
5801     return;
5802
5803   x = ex;
5804   y = ey;
5805
5806   if (phase == 1)
5807     GfxFrame[x][y] = 0;         // restart explosion animation
5808
5809   last_phase = ExplodeDelay[x][y];
5810
5811   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5812
5813   // this can happen if the player leaves an explosion just in time
5814   if (GfxElement[x][y] == EL_UNDEFINED)
5815     GfxElement[x][y] = EL_EMPTY;
5816
5817   border_element = Store2[x][y];
5818   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5819     border_element = StorePlayer[x][y];
5820
5821   if (phase == element_info[border_element].ignition_delay ||
5822       phase == last_phase)
5823   {
5824     boolean border_explosion = FALSE;
5825
5826     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5827         !PLAYER_EXPLOSION_PROTECTED(x, y))
5828     {
5829       KillPlayerUnlessExplosionProtected(x, y);
5830       border_explosion = TRUE;
5831     }
5832     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5833     {
5834       Tile[x][y] = Store2[x][y];
5835       Store2[x][y] = 0;
5836       Bang(x, y);
5837       border_explosion = TRUE;
5838     }
5839     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5840     {
5841       AmoebaToDiamond(x, y);
5842       Store2[x][y] = 0;
5843       border_explosion = TRUE;
5844     }
5845
5846     // if an element just explodes due to another explosion (chain-reaction),
5847     // do not immediately end the new explosion when it was the last frame of
5848     // the explosion (as it would be done in the following "if"-statement!)
5849     if (border_explosion && phase == last_phase)
5850       return;
5851   }
5852
5853   if (phase == last_phase)
5854   {
5855     int element;
5856
5857     element = Tile[x][y] = Store[x][y];
5858     Store[x][y] = Store2[x][y] = 0;
5859     GfxElement[x][y] = EL_UNDEFINED;
5860
5861     // player can escape from explosions and might therefore be still alive
5862     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5863         element <= EL_PLAYER_IS_EXPLODING_4)
5864     {
5865       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5866       int explosion_element = EL_PLAYER_1 + player_nr;
5867       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5868       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5869
5870       if (level.use_explosion_element[player_nr])
5871         explosion_element = level.explosion_element[player_nr];
5872
5873       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5874                     element_info[explosion_element].content.e[xx][yy]);
5875     }
5876
5877     // restore probably existing indestructible background element
5878     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5879       element = Tile[x][y] = Back[x][y];
5880     Back[x][y] = 0;
5881
5882     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5883     GfxDir[x][y] = MV_NONE;
5884     ChangeDelay[x][y] = 0;
5885     ChangePage[x][y] = -1;
5886
5887     CustomValue[x][y] = 0;
5888
5889     InitField_WithBug2(x, y, FALSE);
5890
5891     TEST_DrawLevelField(x, y);
5892
5893     TestIfElementTouchesCustomElement(x, y);
5894
5895     if (GFX_CRUMBLED(element))
5896       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5897
5898     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5899       StorePlayer[x][y] = 0;
5900
5901     if (ELEM_IS_PLAYER(element))
5902       RelocatePlayer(x, y, element);
5903   }
5904   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5905   {
5906     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5907     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5908
5909     if (phase == delay)
5910       TEST_DrawLevelFieldCrumbled(x, y);
5911
5912     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5913     {
5914       DrawLevelElement(x, y, Back[x][y]);
5915       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5916     }
5917     else if (IS_WALKABLE_UNDER(Back[x][y]))
5918     {
5919       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5920       DrawLevelElementThruMask(x, y, Back[x][y]);
5921     }
5922     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5923       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5924   }
5925 }
5926
5927 static void DynaExplode(int ex, int ey)
5928 {
5929   int i, j;
5930   int dynabomb_element = Tile[ex][ey];
5931   int dynabomb_size = 1;
5932   boolean dynabomb_xl = FALSE;
5933   struct PlayerInfo *player;
5934   static int xy[4][2] =
5935   {
5936     { 0, -1 },
5937     { -1, 0 },
5938     { +1, 0 },
5939     { 0, +1 }
5940   };
5941
5942   if (IS_ACTIVE_BOMB(dynabomb_element))
5943   {
5944     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5945     dynabomb_size = player->dynabomb_size;
5946     dynabomb_xl = player->dynabomb_xl;
5947     player->dynabombs_left++;
5948   }
5949
5950   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5951
5952   for (i = 0; i < NUM_DIRECTIONS; i++)
5953   {
5954     for (j = 1; j <= dynabomb_size; j++)
5955     {
5956       int x = ex + j * xy[i][0];
5957       int y = ey + j * xy[i][1];
5958       int element;
5959
5960       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
5961         break;
5962
5963       element = Tile[x][y];
5964
5965       // do not restart explosions of fields with active bombs
5966       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5967         continue;
5968
5969       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5970
5971       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5972           !IS_DIGGABLE(element) && !dynabomb_xl)
5973         break;
5974     }
5975   }
5976 }
5977
5978 void Bang(int x, int y)
5979 {
5980   int element = MovingOrBlocked2Element(x, y);
5981   int explosion_type = EX_TYPE_NORMAL;
5982
5983   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5984   {
5985     struct PlayerInfo *player = PLAYERINFO(x, y);
5986
5987     element = Tile[x][y] = player->initial_element;
5988
5989     if (level.use_explosion_element[player->index_nr])
5990     {
5991       int explosion_element = level.explosion_element[player->index_nr];
5992
5993       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5994         explosion_type = EX_TYPE_CROSS;
5995       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5996         explosion_type = EX_TYPE_CENTER;
5997     }
5998   }
5999
6000   switch (element)
6001   {
6002     case EL_BUG:
6003     case EL_SPACESHIP:
6004     case EL_BD_BUTTERFLY:
6005     case EL_BD_FIREFLY:
6006     case EL_YAMYAM:
6007     case EL_DARK_YAMYAM:
6008     case EL_ROBOT:
6009     case EL_PACMAN:
6010     case EL_MOLE:
6011       RaiseScoreElement(element);
6012       break;
6013
6014     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6015     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6016     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6017     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6018     case EL_DYNABOMB_INCREASE_NUMBER:
6019     case EL_DYNABOMB_INCREASE_SIZE:
6020     case EL_DYNABOMB_INCREASE_POWER:
6021       explosion_type = EX_TYPE_DYNA;
6022       break;
6023
6024     case EL_DC_LANDMINE:
6025       explosion_type = EX_TYPE_CENTER;
6026       break;
6027
6028     case EL_PENGUIN:
6029     case EL_LAMP:
6030     case EL_LAMP_ACTIVE:
6031     case EL_AMOEBA_TO_DIAMOND:
6032       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6033         explosion_type = EX_TYPE_CENTER;
6034       break;
6035
6036     default:
6037       if (element_info[element].explosion_type == EXPLODES_CROSS)
6038         explosion_type = EX_TYPE_CROSS;
6039       else if (element_info[element].explosion_type == EXPLODES_1X1)
6040         explosion_type = EX_TYPE_CENTER;
6041       break;
6042   }
6043
6044   if (explosion_type == EX_TYPE_DYNA)
6045     DynaExplode(x, y);
6046   else
6047     Explode(x, y, EX_PHASE_START, explosion_type);
6048
6049   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6050 }
6051
6052 static void SplashAcid(int x, int y)
6053 {
6054   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6055       (!IN_LEV_FIELD(x - 1, y - 2) ||
6056        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6057     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6058
6059   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6060       (!IN_LEV_FIELD(x + 1, y - 2) ||
6061        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6062     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6063
6064   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6065 }
6066
6067 static void InitBeltMovement(void)
6068 {
6069   static int belt_base_element[4] =
6070   {
6071     EL_CONVEYOR_BELT_1_LEFT,
6072     EL_CONVEYOR_BELT_2_LEFT,
6073     EL_CONVEYOR_BELT_3_LEFT,
6074     EL_CONVEYOR_BELT_4_LEFT
6075   };
6076   static int belt_base_active_element[4] =
6077   {
6078     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6079     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6080     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6081     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6082   };
6083
6084   int x, y, i, j;
6085
6086   // set frame order for belt animation graphic according to belt direction
6087   for (i = 0; i < NUM_BELTS; i++)
6088   {
6089     int belt_nr = i;
6090
6091     for (j = 0; j < NUM_BELT_PARTS; j++)
6092     {
6093       int element = belt_base_active_element[belt_nr] + j;
6094       int graphic_1 = el2img(element);
6095       int graphic_2 = el2panelimg(element);
6096
6097       if (game.belt_dir[i] == MV_LEFT)
6098       {
6099         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6100         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6101       }
6102       else
6103       {
6104         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6105         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6106       }
6107     }
6108   }
6109
6110   SCAN_PLAYFIELD(x, y)
6111   {
6112     int element = Tile[x][y];
6113
6114     for (i = 0; i < NUM_BELTS; i++)
6115     {
6116       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6117       {
6118         int e_belt_nr = getBeltNrFromBeltElement(element);
6119         int belt_nr = i;
6120
6121         if (e_belt_nr == belt_nr)
6122         {
6123           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6124
6125           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6126         }
6127       }
6128     }
6129   }
6130 }
6131
6132 static void ToggleBeltSwitch(int x, int y)
6133 {
6134   static int belt_base_element[4] =
6135   {
6136     EL_CONVEYOR_BELT_1_LEFT,
6137     EL_CONVEYOR_BELT_2_LEFT,
6138     EL_CONVEYOR_BELT_3_LEFT,
6139     EL_CONVEYOR_BELT_4_LEFT
6140   };
6141   static int belt_base_active_element[4] =
6142   {
6143     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6144     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6145     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6146     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6147   };
6148   static int belt_base_switch_element[4] =
6149   {
6150     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6151     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6152     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6153     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6154   };
6155   static int belt_move_dir[4] =
6156   {
6157     MV_LEFT,
6158     MV_NONE,
6159     MV_RIGHT,
6160     MV_NONE,
6161   };
6162
6163   int element = Tile[x][y];
6164   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6165   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6166   int belt_dir = belt_move_dir[belt_dir_nr];
6167   int xx, yy, i;
6168
6169   if (!IS_BELT_SWITCH(element))
6170     return;
6171
6172   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6173   game.belt_dir[belt_nr] = belt_dir;
6174
6175   if (belt_dir_nr == 3)
6176     belt_dir_nr = 1;
6177
6178   // set frame order for belt animation graphic according to belt direction
6179   for (i = 0; i < NUM_BELT_PARTS; i++)
6180   {
6181     int element = belt_base_active_element[belt_nr] + i;
6182     int graphic_1 = el2img(element);
6183     int graphic_2 = el2panelimg(element);
6184
6185     if (belt_dir == MV_LEFT)
6186     {
6187       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6188       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6189     }
6190     else
6191     {
6192       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6193       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6194     }
6195   }
6196
6197   SCAN_PLAYFIELD(xx, yy)
6198   {
6199     int element = Tile[xx][yy];
6200
6201     if (IS_BELT_SWITCH(element))
6202     {
6203       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6204
6205       if (e_belt_nr == belt_nr)
6206       {
6207         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6208         TEST_DrawLevelField(xx, yy);
6209       }
6210     }
6211     else if (IS_BELT(element) && belt_dir != MV_NONE)
6212     {
6213       int e_belt_nr = getBeltNrFromBeltElement(element);
6214
6215       if (e_belt_nr == belt_nr)
6216       {
6217         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6218
6219         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6220         TEST_DrawLevelField(xx, yy);
6221       }
6222     }
6223     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6224     {
6225       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6226
6227       if (e_belt_nr == belt_nr)
6228       {
6229         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6230
6231         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6232         TEST_DrawLevelField(xx, yy);
6233       }
6234     }
6235   }
6236 }
6237
6238 static void ToggleSwitchgateSwitch(int x, int y)
6239 {
6240   int xx, yy;
6241
6242   game.switchgate_pos = !game.switchgate_pos;
6243
6244   SCAN_PLAYFIELD(xx, yy)
6245   {
6246     int element = Tile[xx][yy];
6247
6248     if (element == EL_SWITCHGATE_SWITCH_UP)
6249     {
6250       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6251       TEST_DrawLevelField(xx, yy);
6252     }
6253     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6254     {
6255       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6256       TEST_DrawLevelField(xx, yy);
6257     }
6258     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6259     {
6260       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6261       TEST_DrawLevelField(xx, yy);
6262     }
6263     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6264     {
6265       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6266       TEST_DrawLevelField(xx, yy);
6267     }
6268     else if (element == EL_SWITCHGATE_OPEN ||
6269              element == EL_SWITCHGATE_OPENING)
6270     {
6271       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6272
6273       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6274     }
6275     else if (element == EL_SWITCHGATE_CLOSED ||
6276              element == EL_SWITCHGATE_CLOSING)
6277     {
6278       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6279
6280       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6281     }
6282   }
6283 }
6284
6285 static int getInvisibleActiveFromInvisibleElement(int element)
6286 {
6287   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6288           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6289           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6290           element);
6291 }
6292
6293 static int getInvisibleFromInvisibleActiveElement(int element)
6294 {
6295   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6296           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6297           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6298           element);
6299 }
6300
6301 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6302 {
6303   int x, y;
6304
6305   SCAN_PLAYFIELD(x, y)
6306   {
6307     int element = Tile[x][y];
6308
6309     if (element == EL_LIGHT_SWITCH &&
6310         game.light_time_left > 0)
6311     {
6312       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6313       TEST_DrawLevelField(x, y);
6314     }
6315     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6316              game.light_time_left == 0)
6317     {
6318       Tile[x][y] = EL_LIGHT_SWITCH;
6319       TEST_DrawLevelField(x, y);
6320     }
6321     else if (element == EL_EMC_DRIPPER &&
6322              game.light_time_left > 0)
6323     {
6324       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6325       TEST_DrawLevelField(x, y);
6326     }
6327     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6328              game.light_time_left == 0)
6329     {
6330       Tile[x][y] = EL_EMC_DRIPPER;
6331       TEST_DrawLevelField(x, y);
6332     }
6333     else if (element == EL_INVISIBLE_STEELWALL ||
6334              element == EL_INVISIBLE_WALL ||
6335              element == EL_INVISIBLE_SAND)
6336     {
6337       if (game.light_time_left > 0)
6338         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6339
6340       TEST_DrawLevelField(x, y);
6341
6342       // uncrumble neighbour fields, if needed
6343       if (element == EL_INVISIBLE_SAND)
6344         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6345     }
6346     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6347              element == EL_INVISIBLE_WALL_ACTIVE ||
6348              element == EL_INVISIBLE_SAND_ACTIVE)
6349     {
6350       if (game.light_time_left == 0)
6351         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6352
6353       TEST_DrawLevelField(x, y);
6354
6355       // re-crumble neighbour fields, if needed
6356       if (element == EL_INVISIBLE_SAND)
6357         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6358     }
6359   }
6360 }
6361
6362 static void RedrawAllInvisibleElementsForLenses(void)
6363 {
6364   int x, y;
6365
6366   SCAN_PLAYFIELD(x, y)
6367   {
6368     int element = Tile[x][y];
6369
6370     if (element == EL_EMC_DRIPPER &&
6371         game.lenses_time_left > 0)
6372     {
6373       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6374       TEST_DrawLevelField(x, y);
6375     }
6376     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6377              game.lenses_time_left == 0)
6378     {
6379       Tile[x][y] = EL_EMC_DRIPPER;
6380       TEST_DrawLevelField(x, y);
6381     }
6382     else if (element == EL_INVISIBLE_STEELWALL ||
6383              element == EL_INVISIBLE_WALL ||
6384              element == EL_INVISIBLE_SAND)
6385     {
6386       if (game.lenses_time_left > 0)
6387         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6388
6389       TEST_DrawLevelField(x, y);
6390
6391       // uncrumble neighbour fields, if needed
6392       if (element == EL_INVISIBLE_SAND)
6393         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6394     }
6395     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6396              element == EL_INVISIBLE_WALL_ACTIVE ||
6397              element == EL_INVISIBLE_SAND_ACTIVE)
6398     {
6399       if (game.lenses_time_left == 0)
6400         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6401
6402       TEST_DrawLevelField(x, y);
6403
6404       // re-crumble neighbour fields, if needed
6405       if (element == EL_INVISIBLE_SAND)
6406         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6407     }
6408   }
6409 }
6410
6411 static void RedrawAllInvisibleElementsForMagnifier(void)
6412 {
6413   int x, y;
6414
6415   SCAN_PLAYFIELD(x, y)
6416   {
6417     int element = Tile[x][y];
6418
6419     if (element == EL_EMC_FAKE_GRASS &&
6420         game.magnify_time_left > 0)
6421     {
6422       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6423       TEST_DrawLevelField(x, y);
6424     }
6425     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6426              game.magnify_time_left == 0)
6427     {
6428       Tile[x][y] = EL_EMC_FAKE_GRASS;
6429       TEST_DrawLevelField(x, y);
6430     }
6431     else if (IS_GATE_GRAY(element) &&
6432              game.magnify_time_left > 0)
6433     {
6434       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6435                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6436                     IS_EM_GATE_GRAY(element) ?
6437                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6438                     IS_EMC_GATE_GRAY(element) ?
6439                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6440                     IS_DC_GATE_GRAY(element) ?
6441                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6442                     element);
6443       TEST_DrawLevelField(x, y);
6444     }
6445     else if (IS_GATE_GRAY_ACTIVE(element) &&
6446              game.magnify_time_left == 0)
6447     {
6448       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6449                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6450                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6451                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6452                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6453                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6454                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6455                     EL_DC_GATE_WHITE_GRAY :
6456                     element);
6457       TEST_DrawLevelField(x, y);
6458     }
6459   }
6460 }
6461
6462 static void ToggleLightSwitch(int x, int y)
6463 {
6464   int element = Tile[x][y];
6465
6466   game.light_time_left =
6467     (element == EL_LIGHT_SWITCH ?
6468      level.time_light * FRAMES_PER_SECOND : 0);
6469
6470   RedrawAllLightSwitchesAndInvisibleElements();
6471 }
6472
6473 static void ActivateTimegateSwitch(int x, int y)
6474 {
6475   int xx, yy;
6476
6477   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6478
6479   SCAN_PLAYFIELD(xx, yy)
6480   {
6481     int element = Tile[xx][yy];
6482
6483     if (element == EL_TIMEGATE_CLOSED ||
6484         element == EL_TIMEGATE_CLOSING)
6485     {
6486       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6487       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6488     }
6489
6490     /*
6491     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6492     {
6493       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6494       TEST_DrawLevelField(xx, yy);
6495     }
6496     */
6497
6498   }
6499
6500   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6501                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6502 }
6503
6504 static void Impact(int x, int y)
6505 {
6506   boolean last_line = (y == lev_fieldy - 1);
6507   boolean object_hit = FALSE;
6508   boolean impact = (last_line || object_hit);
6509   int element = Tile[x][y];
6510   int smashed = EL_STEELWALL;
6511
6512   if (!last_line)       // check if element below was hit
6513   {
6514     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6515       return;
6516
6517     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6518                                          MovDir[x][y + 1] != MV_DOWN ||
6519                                          MovPos[x][y + 1] <= TILEY / 2));
6520
6521     // do not smash moving elements that left the smashed field in time
6522     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6523         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6524       object_hit = FALSE;
6525
6526 #if USE_QUICKSAND_IMPACT_BUGFIX
6527     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6528     {
6529       RemoveMovingField(x, y + 1);
6530       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6531       Tile[x][y + 2] = EL_ROCK;
6532       TEST_DrawLevelField(x, y + 2);
6533
6534       object_hit = TRUE;
6535     }
6536
6537     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6538     {
6539       RemoveMovingField(x, y + 1);
6540       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6541       Tile[x][y + 2] = EL_ROCK;
6542       TEST_DrawLevelField(x, y + 2);
6543
6544       object_hit = TRUE;
6545     }
6546 #endif
6547
6548     if (object_hit)
6549       smashed = MovingOrBlocked2Element(x, y + 1);
6550
6551     impact = (last_line || object_hit);
6552   }
6553
6554   if (!last_line && smashed == EL_ACID) // element falls into acid
6555   {
6556     SplashAcid(x, y + 1);
6557     return;
6558   }
6559
6560   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6561   // only reset graphic animation if graphic really changes after impact
6562   if (impact &&
6563       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6564   {
6565     ResetGfxAnimation(x, y);
6566     TEST_DrawLevelField(x, y);
6567   }
6568
6569   if (impact && CAN_EXPLODE_IMPACT(element))
6570   {
6571     Bang(x, y);
6572     return;
6573   }
6574   else if (impact && element == EL_PEARL &&
6575            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6576   {
6577     ResetGfxAnimation(x, y);
6578
6579     Tile[x][y] = EL_PEARL_BREAKING;
6580     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6581     return;
6582   }
6583   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6584   {
6585     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6586
6587     return;
6588   }
6589
6590   if (impact && element == EL_AMOEBA_DROP)
6591   {
6592     if (object_hit && IS_PLAYER(x, y + 1))
6593       KillPlayerUnlessEnemyProtected(x, y + 1);
6594     else if (object_hit && smashed == EL_PENGUIN)
6595       Bang(x, y + 1);
6596     else
6597     {
6598       Tile[x][y] = EL_AMOEBA_GROWING;
6599       Store[x][y] = EL_AMOEBA_WET;
6600
6601       ResetRandomAnimationValue(x, y);
6602     }
6603     return;
6604   }
6605
6606   if (object_hit)               // check which object was hit
6607   {
6608     if ((CAN_PASS_MAGIC_WALL(element) && 
6609          (smashed == EL_MAGIC_WALL ||
6610           smashed == EL_BD_MAGIC_WALL)) ||
6611         (CAN_PASS_DC_MAGIC_WALL(element) &&
6612          smashed == EL_DC_MAGIC_WALL))
6613     {
6614       int xx, yy;
6615       int activated_magic_wall =
6616         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6617          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6618          EL_DC_MAGIC_WALL_ACTIVE);
6619
6620       // activate magic wall / mill
6621       SCAN_PLAYFIELD(xx, yy)
6622       {
6623         if (Tile[xx][yy] == smashed)
6624           Tile[xx][yy] = activated_magic_wall;
6625       }
6626
6627       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6628       game.magic_wall_active = TRUE;
6629
6630       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6631                             SND_MAGIC_WALL_ACTIVATING :
6632                             smashed == EL_BD_MAGIC_WALL ?
6633                             SND_BD_MAGIC_WALL_ACTIVATING :
6634                             SND_DC_MAGIC_WALL_ACTIVATING));
6635     }
6636
6637     if (IS_PLAYER(x, y + 1))
6638     {
6639       if (CAN_SMASH_PLAYER(element))
6640       {
6641         KillPlayerUnlessEnemyProtected(x, y + 1);
6642         return;
6643       }
6644     }
6645     else if (smashed == EL_PENGUIN)
6646     {
6647       if (CAN_SMASH_PLAYER(element))
6648       {
6649         Bang(x, y + 1);
6650         return;
6651       }
6652     }
6653     else if (element == EL_BD_DIAMOND)
6654     {
6655       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6656       {
6657         Bang(x, y + 1);
6658         return;
6659       }
6660     }
6661     else if (((element == EL_SP_INFOTRON ||
6662                element == EL_SP_ZONK) &&
6663               (smashed == EL_SP_SNIKSNAK ||
6664                smashed == EL_SP_ELECTRON ||
6665                smashed == EL_SP_DISK_ORANGE)) ||
6666              (element == EL_SP_INFOTRON &&
6667               smashed == EL_SP_DISK_YELLOW))
6668     {
6669       Bang(x, y + 1);
6670       return;
6671     }
6672     else if (CAN_SMASH_EVERYTHING(element))
6673     {
6674       if (IS_CLASSIC_ENEMY(smashed) ||
6675           CAN_EXPLODE_SMASHED(smashed))
6676       {
6677         Bang(x, y + 1);
6678         return;
6679       }
6680       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6681       {
6682         if (smashed == EL_LAMP ||
6683             smashed == EL_LAMP_ACTIVE)
6684         {
6685           Bang(x, y + 1);
6686           return;
6687         }
6688         else if (smashed == EL_NUT)
6689         {
6690           Tile[x][y + 1] = EL_NUT_BREAKING;
6691           PlayLevelSound(x, y, SND_NUT_BREAKING);
6692           RaiseScoreElement(EL_NUT);
6693           return;
6694         }
6695         else if (smashed == EL_PEARL)
6696         {
6697           ResetGfxAnimation(x, y);
6698
6699           Tile[x][y + 1] = EL_PEARL_BREAKING;
6700           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6701           return;
6702         }
6703         else if (smashed == EL_DIAMOND)
6704         {
6705           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6706           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6707           return;
6708         }
6709         else if (IS_BELT_SWITCH(smashed))
6710         {
6711           ToggleBeltSwitch(x, y + 1);
6712         }
6713         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6714                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6715                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6716                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6717         {
6718           ToggleSwitchgateSwitch(x, y + 1);
6719         }
6720         else if (smashed == EL_LIGHT_SWITCH ||
6721                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6722         {
6723           ToggleLightSwitch(x, y + 1);
6724         }
6725         else
6726         {
6727           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6728
6729           CheckElementChangeBySide(x, y + 1, smashed, element,
6730                                    CE_SWITCHED, CH_SIDE_TOP);
6731           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6732                                             CH_SIDE_TOP);
6733         }
6734       }
6735       else
6736       {
6737         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6738       }
6739     }
6740   }
6741
6742   // play sound of magic wall / mill
6743   if (!last_line &&
6744       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6745        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6746        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6747   {
6748     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6749       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6750     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6751       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6752     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6753       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6754
6755     return;
6756   }
6757
6758   // play sound of object that hits the ground
6759   if (last_line || object_hit)
6760     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6761 }
6762
6763 static void TurnRoundExt(int x, int y)
6764 {
6765   static struct
6766   {
6767     int dx, dy;
6768   } move_xy[] =
6769   {
6770     {  0,  0 },
6771     { -1,  0 },
6772     { +1,  0 },
6773     {  0,  0 },
6774     {  0, -1 },
6775     {  0,  0 }, { 0, 0 }, { 0, 0 },
6776     {  0, +1 }
6777   };
6778   static struct
6779   {
6780     int left, right, back;
6781   } turn[] =
6782   {
6783     { 0,        0,              0        },
6784     { MV_DOWN,  MV_UP,          MV_RIGHT },
6785     { MV_UP,    MV_DOWN,        MV_LEFT  },
6786     { 0,        0,              0        },
6787     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6788     { 0,        0,              0        },
6789     { 0,        0,              0        },
6790     { 0,        0,              0        },
6791     { MV_RIGHT, MV_LEFT,        MV_UP    }
6792   };
6793
6794   int element = Tile[x][y];
6795   int move_pattern = element_info[element].move_pattern;
6796
6797   int old_move_dir = MovDir[x][y];
6798   int left_dir  = turn[old_move_dir].left;
6799   int right_dir = turn[old_move_dir].right;
6800   int back_dir  = turn[old_move_dir].back;
6801
6802   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6803   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6804   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6805   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6806
6807   int left_x  = x + left_dx,  left_y  = y + left_dy;
6808   int right_x = x + right_dx, right_y = y + right_dy;
6809   int move_x  = x + move_dx,  move_y  = y + move_dy;
6810
6811   int xx, yy;
6812
6813   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6814   {
6815     TestIfBadThingTouchesOtherBadThing(x, y);
6816
6817     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6818       MovDir[x][y] = right_dir;
6819     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6820       MovDir[x][y] = left_dir;
6821
6822     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6823       MovDelay[x][y] = 9;
6824     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6825       MovDelay[x][y] = 1;
6826   }
6827   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6828   {
6829     TestIfBadThingTouchesOtherBadThing(x, y);
6830
6831     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6832       MovDir[x][y] = left_dir;
6833     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6834       MovDir[x][y] = right_dir;
6835
6836     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6837       MovDelay[x][y] = 9;
6838     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6839       MovDelay[x][y] = 1;
6840   }
6841   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6842   {
6843     TestIfBadThingTouchesOtherBadThing(x, y);
6844
6845     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6846       MovDir[x][y] = left_dir;
6847     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6848       MovDir[x][y] = right_dir;
6849
6850     if (MovDir[x][y] != old_move_dir)
6851       MovDelay[x][y] = 9;
6852   }
6853   else if (element == EL_YAMYAM)
6854   {
6855     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6856     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6857
6858     if (can_turn_left && can_turn_right)
6859       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6860     else if (can_turn_left)
6861       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6862     else if (can_turn_right)
6863       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6864     else
6865       MovDir[x][y] = back_dir;
6866
6867     MovDelay[x][y] = 16 + 16 * RND(3);
6868   }
6869   else if (element == EL_DARK_YAMYAM)
6870   {
6871     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6872                                                          left_x, left_y);
6873     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6874                                                          right_x, right_y);
6875
6876     if (can_turn_left && can_turn_right)
6877       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6878     else if (can_turn_left)
6879       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6880     else if (can_turn_right)
6881       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6882     else
6883       MovDir[x][y] = back_dir;
6884
6885     MovDelay[x][y] = 16 + 16 * RND(3);
6886   }
6887   else if (element == EL_PACMAN)
6888   {
6889     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6890     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6891
6892     if (can_turn_left && can_turn_right)
6893       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6894     else if (can_turn_left)
6895       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6896     else if (can_turn_right)
6897       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6898     else
6899       MovDir[x][y] = back_dir;
6900
6901     MovDelay[x][y] = 6 + RND(40);
6902   }
6903   else if (element == EL_PIG)
6904   {
6905     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6906     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6907     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6908     boolean should_turn_left, should_turn_right, should_move_on;
6909     int rnd_value = 24;
6910     int rnd = RND(rnd_value);
6911
6912     should_turn_left = (can_turn_left &&
6913                         (!can_move_on ||
6914                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6915                                                    y + back_dy + left_dy)));
6916     should_turn_right = (can_turn_right &&
6917                          (!can_move_on ||
6918                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6919                                                     y + back_dy + right_dy)));
6920     should_move_on = (can_move_on &&
6921                       (!can_turn_left ||
6922                        !can_turn_right ||
6923                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6924                                                  y + move_dy + left_dy) ||
6925                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6926                                                  y + move_dy + right_dy)));
6927
6928     if (should_turn_left || should_turn_right || should_move_on)
6929     {
6930       if (should_turn_left && should_turn_right && should_move_on)
6931         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6932                         rnd < 2 * rnd_value / 3 ? right_dir :
6933                         old_move_dir);
6934       else if (should_turn_left && should_turn_right)
6935         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6936       else if (should_turn_left && should_move_on)
6937         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6938       else if (should_turn_right && should_move_on)
6939         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6940       else if (should_turn_left)
6941         MovDir[x][y] = left_dir;
6942       else if (should_turn_right)
6943         MovDir[x][y] = right_dir;
6944       else if (should_move_on)
6945         MovDir[x][y] = old_move_dir;
6946     }
6947     else if (can_move_on && rnd > rnd_value / 8)
6948       MovDir[x][y] = old_move_dir;
6949     else if (can_turn_left && can_turn_right)
6950       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6951     else if (can_turn_left && rnd > rnd_value / 8)
6952       MovDir[x][y] = left_dir;
6953     else if (can_turn_right && rnd > rnd_value/8)
6954       MovDir[x][y] = right_dir;
6955     else
6956       MovDir[x][y] = back_dir;
6957
6958     xx = x + move_xy[MovDir[x][y]].dx;
6959     yy = y + move_xy[MovDir[x][y]].dy;
6960
6961     if (!IN_LEV_FIELD(xx, yy) ||
6962         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
6963       MovDir[x][y] = old_move_dir;
6964
6965     MovDelay[x][y] = 0;
6966   }
6967   else if (element == EL_DRAGON)
6968   {
6969     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6970     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6971     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6972     int rnd_value = 24;
6973     int rnd = RND(rnd_value);
6974
6975     if (can_move_on && rnd > rnd_value / 8)
6976       MovDir[x][y] = old_move_dir;
6977     else if (can_turn_left && can_turn_right)
6978       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6979     else if (can_turn_left && rnd > rnd_value / 8)
6980       MovDir[x][y] = left_dir;
6981     else if (can_turn_right && rnd > rnd_value / 8)
6982       MovDir[x][y] = right_dir;
6983     else
6984       MovDir[x][y] = back_dir;
6985
6986     xx = x + move_xy[MovDir[x][y]].dx;
6987     yy = y + move_xy[MovDir[x][y]].dy;
6988
6989     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6990       MovDir[x][y] = old_move_dir;
6991
6992     MovDelay[x][y] = 0;
6993   }
6994   else if (element == EL_MOLE)
6995   {
6996     boolean can_move_on =
6997       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6998                             IS_AMOEBOID(Tile[move_x][move_y]) ||
6999                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7000     if (!can_move_on)
7001     {
7002       boolean can_turn_left =
7003         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7004                               IS_AMOEBOID(Tile[left_x][left_y])));
7005
7006       boolean can_turn_right =
7007         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7008                               IS_AMOEBOID(Tile[right_x][right_y])));
7009
7010       if (can_turn_left && can_turn_right)
7011         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7012       else if (can_turn_left)
7013         MovDir[x][y] = left_dir;
7014       else
7015         MovDir[x][y] = right_dir;
7016     }
7017
7018     if (MovDir[x][y] != old_move_dir)
7019       MovDelay[x][y] = 9;
7020   }
7021   else if (element == EL_BALLOON)
7022   {
7023     MovDir[x][y] = game.wind_direction;
7024     MovDelay[x][y] = 0;
7025   }
7026   else if (element == EL_SPRING)
7027   {
7028     if (MovDir[x][y] & MV_HORIZONTAL)
7029     {
7030       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7031           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7032       {
7033         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7034         ResetGfxAnimation(move_x, move_y);
7035         TEST_DrawLevelField(move_x, move_y);
7036
7037         MovDir[x][y] = back_dir;
7038       }
7039       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7040                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7041         MovDir[x][y] = MV_NONE;
7042     }
7043
7044     MovDelay[x][y] = 0;
7045   }
7046   else if (element == EL_ROBOT ||
7047            element == EL_SATELLITE ||
7048            element == EL_PENGUIN ||
7049            element == EL_EMC_ANDROID)
7050   {
7051     int attr_x = -1, attr_y = -1;
7052
7053     if (game.all_players_gone)
7054     {
7055       attr_x = game.exit_x;
7056       attr_y = game.exit_y;
7057     }
7058     else
7059     {
7060       int i;
7061
7062       for (i = 0; i < MAX_PLAYERS; i++)
7063       {
7064         struct PlayerInfo *player = &stored_player[i];
7065         int jx = player->jx, jy = player->jy;
7066
7067         if (!player->active)
7068           continue;
7069
7070         if (attr_x == -1 ||
7071             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7072         {
7073           attr_x = jx;
7074           attr_y = jy;
7075         }
7076       }
7077     }
7078
7079     if (element == EL_ROBOT &&
7080         game.robot_wheel_x >= 0 &&
7081         game.robot_wheel_y >= 0 &&
7082         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7083          game.engine_version < VERSION_IDENT(3,1,0,0)))
7084     {
7085       attr_x = game.robot_wheel_x;
7086       attr_y = game.robot_wheel_y;
7087     }
7088
7089     if (element == EL_PENGUIN)
7090     {
7091       int i;
7092       static int xy[4][2] =
7093       {
7094         { 0, -1 },
7095         { -1, 0 },
7096         { +1, 0 },
7097         { 0, +1 }
7098       };
7099
7100       for (i = 0; i < NUM_DIRECTIONS; i++)
7101       {
7102         int ex = x + xy[i][0];
7103         int ey = y + xy[i][1];
7104
7105         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7106                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7107                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7108                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7109         {
7110           attr_x = ex;
7111           attr_y = ey;
7112           break;
7113         }
7114       }
7115     }
7116
7117     MovDir[x][y] = MV_NONE;
7118     if (attr_x < x)
7119       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7120     else if (attr_x > x)
7121       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7122     if (attr_y < y)
7123       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7124     else if (attr_y > y)
7125       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7126
7127     if (element == EL_ROBOT)
7128     {
7129       int newx, newy;
7130
7131       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7132         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7133       Moving2Blocked(x, y, &newx, &newy);
7134
7135       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7136         MovDelay[x][y] = 8 + 8 * !RND(3);
7137       else
7138         MovDelay[x][y] = 16;
7139     }
7140     else if (element == EL_PENGUIN)
7141     {
7142       int newx, newy;
7143
7144       MovDelay[x][y] = 1;
7145
7146       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7147       {
7148         boolean first_horiz = RND(2);
7149         int new_move_dir = MovDir[x][y];
7150
7151         MovDir[x][y] =
7152           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7153         Moving2Blocked(x, y, &newx, &newy);
7154
7155         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7156           return;
7157
7158         MovDir[x][y] =
7159           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7160         Moving2Blocked(x, y, &newx, &newy);
7161
7162         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7163           return;
7164
7165         MovDir[x][y] = old_move_dir;
7166         return;
7167       }
7168     }
7169     else if (element == EL_SATELLITE)
7170     {
7171       int newx, newy;
7172
7173       MovDelay[x][y] = 1;
7174
7175       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7176       {
7177         boolean first_horiz = RND(2);
7178         int new_move_dir = MovDir[x][y];
7179
7180         MovDir[x][y] =
7181           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7182         Moving2Blocked(x, y, &newx, &newy);
7183
7184         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7185           return;
7186
7187         MovDir[x][y] =
7188           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7189         Moving2Blocked(x, y, &newx, &newy);
7190
7191         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7192           return;
7193
7194         MovDir[x][y] = old_move_dir;
7195         return;
7196       }
7197     }
7198     else if (element == EL_EMC_ANDROID)
7199     {
7200       static int check_pos[16] =
7201       {
7202         -1,             //  0 => (invalid)
7203         7,              //  1 => MV_LEFT
7204         3,              //  2 => MV_RIGHT
7205         -1,             //  3 => (invalid)
7206         1,              //  4 =>            MV_UP
7207         0,              //  5 => MV_LEFT  | MV_UP
7208         2,              //  6 => MV_RIGHT | MV_UP
7209         -1,             //  7 => (invalid)
7210         5,              //  8 =>            MV_DOWN
7211         6,              //  9 => MV_LEFT  | MV_DOWN
7212         4,              // 10 => MV_RIGHT | MV_DOWN
7213         -1,             // 11 => (invalid)
7214         -1,             // 12 => (invalid)
7215         -1,             // 13 => (invalid)
7216         -1,             // 14 => (invalid)
7217         -1,             // 15 => (invalid)
7218       };
7219       static struct
7220       {
7221         int dx, dy;
7222         int dir;
7223       } check_xy[8] =
7224       {
7225         { -1, -1,       MV_LEFT  | MV_UP   },
7226         {  0, -1,                  MV_UP   },
7227         { +1, -1,       MV_RIGHT | MV_UP   },
7228         { +1,  0,       MV_RIGHT           },
7229         { +1, +1,       MV_RIGHT | MV_DOWN },
7230         {  0, +1,                  MV_DOWN },
7231         { -1, +1,       MV_LEFT  | MV_DOWN },
7232         { -1,  0,       MV_LEFT            },
7233       };
7234       int start_pos, check_order;
7235       boolean can_clone = FALSE;
7236       int i;
7237
7238       // check if there is any free field around current position
7239       for (i = 0; i < 8; i++)
7240       {
7241         int newx = x + check_xy[i].dx;
7242         int newy = y + check_xy[i].dy;
7243
7244         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7245         {
7246           can_clone = TRUE;
7247
7248           break;
7249         }
7250       }
7251
7252       if (can_clone)            // randomly find an element to clone
7253       {
7254         can_clone = FALSE;
7255
7256         start_pos = check_pos[RND(8)];
7257         check_order = (RND(2) ? -1 : +1);
7258
7259         for (i = 0; i < 8; i++)
7260         {
7261           int pos_raw = start_pos + i * check_order;
7262           int pos = (pos_raw + 8) % 8;
7263           int newx = x + check_xy[pos].dx;
7264           int newy = y + check_xy[pos].dy;
7265
7266           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7267           {
7268             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7269             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7270
7271             Store[x][y] = Tile[newx][newy];
7272
7273             can_clone = TRUE;
7274
7275             break;
7276           }
7277         }
7278       }
7279
7280       if (can_clone)            // randomly find a direction to move
7281       {
7282         can_clone = FALSE;
7283
7284         start_pos = check_pos[RND(8)];
7285         check_order = (RND(2) ? -1 : +1);
7286
7287         for (i = 0; i < 8; i++)
7288         {
7289           int pos_raw = start_pos + i * check_order;
7290           int pos = (pos_raw + 8) % 8;
7291           int newx = x + check_xy[pos].dx;
7292           int newy = y + check_xy[pos].dy;
7293           int new_move_dir = check_xy[pos].dir;
7294
7295           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7296           {
7297             MovDir[x][y] = new_move_dir;
7298             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7299
7300             can_clone = TRUE;
7301
7302             break;
7303           }
7304         }
7305       }
7306
7307       if (can_clone)            // cloning and moving successful
7308         return;
7309
7310       // cannot clone -- try to move towards player
7311
7312       start_pos = check_pos[MovDir[x][y] & 0x0f];
7313       check_order = (RND(2) ? -1 : +1);
7314
7315       for (i = 0; i < 3; i++)
7316       {
7317         // first check start_pos, then previous/next or (next/previous) pos
7318         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7319         int pos = (pos_raw + 8) % 8;
7320         int newx = x + check_xy[pos].dx;
7321         int newy = y + check_xy[pos].dy;
7322         int new_move_dir = check_xy[pos].dir;
7323
7324         if (IS_PLAYER(newx, newy))
7325           break;
7326
7327         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7328         {
7329           MovDir[x][y] = new_move_dir;
7330           MovDelay[x][y] = level.android_move_time * 8 + 1;
7331
7332           break;
7333         }
7334       }
7335     }
7336   }
7337   else if (move_pattern == MV_TURNING_LEFT ||
7338            move_pattern == MV_TURNING_RIGHT ||
7339            move_pattern == MV_TURNING_LEFT_RIGHT ||
7340            move_pattern == MV_TURNING_RIGHT_LEFT ||
7341            move_pattern == MV_TURNING_RANDOM ||
7342            move_pattern == MV_ALL_DIRECTIONS)
7343   {
7344     boolean can_turn_left =
7345       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7346     boolean can_turn_right =
7347       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7348
7349     if (element_info[element].move_stepsize == 0)       // "not moving"
7350       return;
7351
7352     if (move_pattern == MV_TURNING_LEFT)
7353       MovDir[x][y] = left_dir;
7354     else if (move_pattern == MV_TURNING_RIGHT)
7355       MovDir[x][y] = right_dir;
7356     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7357       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7358     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7359       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7360     else if (move_pattern == MV_TURNING_RANDOM)
7361       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7362                       can_turn_right && !can_turn_left ? right_dir :
7363                       RND(2) ? left_dir : right_dir);
7364     else if (can_turn_left && can_turn_right)
7365       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7366     else if (can_turn_left)
7367       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7368     else if (can_turn_right)
7369       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7370     else
7371       MovDir[x][y] = back_dir;
7372
7373     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7374   }
7375   else if (move_pattern == MV_HORIZONTAL ||
7376            move_pattern == MV_VERTICAL)
7377   {
7378     if (move_pattern & old_move_dir)
7379       MovDir[x][y] = back_dir;
7380     else if (move_pattern == MV_HORIZONTAL)
7381       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7382     else if (move_pattern == MV_VERTICAL)
7383       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7384
7385     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7386   }
7387   else if (move_pattern & MV_ANY_DIRECTION)
7388   {
7389     MovDir[x][y] = move_pattern;
7390     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7391   }
7392   else if (move_pattern & MV_WIND_DIRECTION)
7393   {
7394     MovDir[x][y] = game.wind_direction;
7395     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7396   }
7397   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7398   {
7399     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7400       MovDir[x][y] = left_dir;
7401     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7402       MovDir[x][y] = right_dir;
7403
7404     if (MovDir[x][y] != old_move_dir)
7405       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7406   }
7407   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7408   {
7409     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7410       MovDir[x][y] = right_dir;
7411     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7412       MovDir[x][y] = left_dir;
7413
7414     if (MovDir[x][y] != old_move_dir)
7415       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7416   }
7417   else if (move_pattern == MV_TOWARDS_PLAYER ||
7418            move_pattern == MV_AWAY_FROM_PLAYER)
7419   {
7420     int attr_x = -1, attr_y = -1;
7421     int newx, newy;
7422     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7423
7424     if (game.all_players_gone)
7425     {
7426       attr_x = game.exit_x;
7427       attr_y = game.exit_y;
7428     }
7429     else
7430     {
7431       int i;
7432
7433       for (i = 0; i < MAX_PLAYERS; i++)
7434       {
7435         struct PlayerInfo *player = &stored_player[i];
7436         int jx = player->jx, jy = player->jy;
7437
7438         if (!player->active)
7439           continue;
7440
7441         if (attr_x == -1 ||
7442             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7443         {
7444           attr_x = jx;
7445           attr_y = jy;
7446         }
7447       }
7448     }
7449
7450     MovDir[x][y] = MV_NONE;
7451     if (attr_x < x)
7452       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7453     else if (attr_x > x)
7454       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7455     if (attr_y < y)
7456       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7457     else if (attr_y > y)
7458       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7459
7460     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7461
7462     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7463     {
7464       boolean first_horiz = RND(2);
7465       int new_move_dir = MovDir[x][y];
7466
7467       if (element_info[element].move_stepsize == 0)     // "not moving"
7468       {
7469         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7470         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7471
7472         return;
7473       }
7474
7475       MovDir[x][y] =
7476         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7477       Moving2Blocked(x, y, &newx, &newy);
7478
7479       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7480         return;
7481
7482       MovDir[x][y] =
7483         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7484       Moving2Blocked(x, y, &newx, &newy);
7485
7486       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7487         return;
7488
7489       MovDir[x][y] = old_move_dir;
7490     }
7491   }
7492   else if (move_pattern == MV_WHEN_PUSHED ||
7493            move_pattern == MV_WHEN_DROPPED)
7494   {
7495     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7496       MovDir[x][y] = MV_NONE;
7497
7498     MovDelay[x][y] = 0;
7499   }
7500   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7501   {
7502     static int test_xy[7][2] =
7503     {
7504       { 0, -1 },
7505       { -1, 0 },
7506       { +1, 0 },
7507       { 0, +1 },
7508       { 0, -1 },
7509       { -1, 0 },
7510       { +1, 0 },
7511     };
7512     static int test_dir[7] =
7513     {
7514       MV_UP,
7515       MV_LEFT,
7516       MV_RIGHT,
7517       MV_DOWN,
7518       MV_UP,
7519       MV_LEFT,
7520       MV_RIGHT,
7521     };
7522     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7523     int move_preference = -1000000;     // start with very low preference
7524     int new_move_dir = MV_NONE;
7525     int start_test = RND(4);
7526     int i;
7527
7528     for (i = 0; i < NUM_DIRECTIONS; i++)
7529     {
7530       int move_dir = test_dir[start_test + i];
7531       int move_dir_preference;
7532
7533       xx = x + test_xy[start_test + i][0];
7534       yy = y + test_xy[start_test + i][1];
7535
7536       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7537           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7538       {
7539         new_move_dir = move_dir;
7540
7541         break;
7542       }
7543
7544       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7545         continue;
7546
7547       move_dir_preference = -1 * RunnerVisit[xx][yy];
7548       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7549         move_dir_preference = PlayerVisit[xx][yy];
7550
7551       if (move_dir_preference > move_preference)
7552       {
7553         // prefer field that has not been visited for the longest time
7554         move_preference = move_dir_preference;
7555         new_move_dir = move_dir;
7556       }
7557       else if (move_dir_preference == move_preference &&
7558                move_dir == old_move_dir)
7559       {
7560         // prefer last direction when all directions are preferred equally
7561         move_preference = move_dir_preference;
7562         new_move_dir = move_dir;
7563       }
7564     }
7565
7566     MovDir[x][y] = new_move_dir;
7567     if (old_move_dir != new_move_dir)
7568       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7569   }
7570 }
7571
7572 static void TurnRound(int x, int y)
7573 {
7574   int direction = MovDir[x][y];
7575
7576   TurnRoundExt(x, y);
7577
7578   GfxDir[x][y] = MovDir[x][y];
7579
7580   if (direction != MovDir[x][y])
7581     GfxFrame[x][y] = 0;
7582
7583   if (MovDelay[x][y])
7584     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7585
7586   ResetGfxFrame(x, y);
7587 }
7588
7589 static boolean JustBeingPushed(int x, int y)
7590 {
7591   int i;
7592
7593   for (i = 0; i < MAX_PLAYERS; i++)
7594   {
7595     struct PlayerInfo *player = &stored_player[i];
7596
7597     if (player->active && player->is_pushing && player->MovPos)
7598     {
7599       int next_jx = player->jx + (player->jx - player->last_jx);
7600       int next_jy = player->jy + (player->jy - player->last_jy);
7601
7602       if (x == next_jx && y == next_jy)
7603         return TRUE;
7604     }
7605   }
7606
7607   return FALSE;
7608 }
7609
7610 static void StartMoving(int x, int y)
7611 {
7612   boolean started_moving = FALSE;       // some elements can fall _and_ move
7613   int element = Tile[x][y];
7614
7615   if (Stop[x][y])
7616     return;
7617
7618   if (MovDelay[x][y] == 0)
7619     GfxAction[x][y] = ACTION_DEFAULT;
7620
7621   if (CAN_FALL(element) && y < lev_fieldy - 1)
7622   {
7623     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7624         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7625       if (JustBeingPushed(x, y))
7626         return;
7627
7628     if (element == EL_QUICKSAND_FULL)
7629     {
7630       if (IS_FREE(x, y + 1))
7631       {
7632         InitMovingField(x, y, MV_DOWN);
7633         started_moving = TRUE;
7634
7635         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7636 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7637         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7638           Store[x][y] = EL_ROCK;
7639 #else
7640         Store[x][y] = EL_ROCK;
7641 #endif
7642
7643         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7644       }
7645       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7646       {
7647         if (!MovDelay[x][y])
7648         {
7649           MovDelay[x][y] = TILEY + 1;
7650
7651           ResetGfxAnimation(x, y);
7652           ResetGfxAnimation(x, y + 1);
7653         }
7654
7655         if (MovDelay[x][y])
7656         {
7657           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7658           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7659
7660           MovDelay[x][y]--;
7661           if (MovDelay[x][y])
7662             return;
7663         }
7664
7665         Tile[x][y] = EL_QUICKSAND_EMPTY;
7666         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7667         Store[x][y + 1] = Store[x][y];
7668         Store[x][y] = 0;
7669
7670         PlayLevelSoundAction(x, y, ACTION_FILLING);
7671       }
7672       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7673       {
7674         if (!MovDelay[x][y])
7675         {
7676           MovDelay[x][y] = TILEY + 1;
7677
7678           ResetGfxAnimation(x, y);
7679           ResetGfxAnimation(x, y + 1);
7680         }
7681
7682         if (MovDelay[x][y])
7683         {
7684           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7685           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7686
7687           MovDelay[x][y]--;
7688           if (MovDelay[x][y])
7689             return;
7690         }
7691
7692         Tile[x][y] = EL_QUICKSAND_EMPTY;
7693         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7694         Store[x][y + 1] = Store[x][y];
7695         Store[x][y] = 0;
7696
7697         PlayLevelSoundAction(x, y, ACTION_FILLING);
7698       }
7699     }
7700     else if (element == EL_QUICKSAND_FAST_FULL)
7701     {
7702       if (IS_FREE(x, y + 1))
7703       {
7704         InitMovingField(x, y, MV_DOWN);
7705         started_moving = TRUE;
7706
7707         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7708 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7709         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7710           Store[x][y] = EL_ROCK;
7711 #else
7712         Store[x][y] = EL_ROCK;
7713 #endif
7714
7715         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7716       }
7717       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7718       {
7719         if (!MovDelay[x][y])
7720         {
7721           MovDelay[x][y] = TILEY + 1;
7722
7723           ResetGfxAnimation(x, y);
7724           ResetGfxAnimation(x, y + 1);
7725         }
7726
7727         if (MovDelay[x][y])
7728         {
7729           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7730           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7731
7732           MovDelay[x][y]--;
7733           if (MovDelay[x][y])
7734             return;
7735         }
7736
7737         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7738         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7739         Store[x][y + 1] = Store[x][y];
7740         Store[x][y] = 0;
7741
7742         PlayLevelSoundAction(x, y, ACTION_FILLING);
7743       }
7744       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7745       {
7746         if (!MovDelay[x][y])
7747         {
7748           MovDelay[x][y] = TILEY + 1;
7749
7750           ResetGfxAnimation(x, y);
7751           ResetGfxAnimation(x, y + 1);
7752         }
7753
7754         if (MovDelay[x][y])
7755         {
7756           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7757           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7758
7759           MovDelay[x][y]--;
7760           if (MovDelay[x][y])
7761             return;
7762         }
7763
7764         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7765         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7766         Store[x][y + 1] = Store[x][y];
7767         Store[x][y] = 0;
7768
7769         PlayLevelSoundAction(x, y, ACTION_FILLING);
7770       }
7771     }
7772     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7773              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7774     {
7775       InitMovingField(x, y, MV_DOWN);
7776       started_moving = TRUE;
7777
7778       Tile[x][y] = EL_QUICKSAND_FILLING;
7779       Store[x][y] = element;
7780
7781       PlayLevelSoundAction(x, y, ACTION_FILLING);
7782     }
7783     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7784              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7785     {
7786       InitMovingField(x, y, MV_DOWN);
7787       started_moving = TRUE;
7788
7789       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7790       Store[x][y] = element;
7791
7792       PlayLevelSoundAction(x, y, ACTION_FILLING);
7793     }
7794     else if (element == EL_MAGIC_WALL_FULL)
7795     {
7796       if (IS_FREE(x, y + 1))
7797       {
7798         InitMovingField(x, y, MV_DOWN);
7799         started_moving = TRUE;
7800
7801         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7802         Store[x][y] = EL_CHANGED(Store[x][y]);
7803       }
7804       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7805       {
7806         if (!MovDelay[x][y])
7807           MovDelay[x][y] = TILEY / 4 + 1;
7808
7809         if (MovDelay[x][y])
7810         {
7811           MovDelay[x][y]--;
7812           if (MovDelay[x][y])
7813             return;
7814         }
7815
7816         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7817         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7818         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7819         Store[x][y] = 0;
7820       }
7821     }
7822     else if (element == EL_BD_MAGIC_WALL_FULL)
7823     {
7824       if (IS_FREE(x, y + 1))
7825       {
7826         InitMovingField(x, y, MV_DOWN);
7827         started_moving = TRUE;
7828
7829         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7830         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7831       }
7832       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7833       {
7834         if (!MovDelay[x][y])
7835           MovDelay[x][y] = TILEY / 4 + 1;
7836
7837         if (MovDelay[x][y])
7838         {
7839           MovDelay[x][y]--;
7840           if (MovDelay[x][y])
7841             return;
7842         }
7843
7844         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7845         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7846         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7847         Store[x][y] = 0;
7848       }
7849     }
7850     else if (element == EL_DC_MAGIC_WALL_FULL)
7851     {
7852       if (IS_FREE(x, y + 1))
7853       {
7854         InitMovingField(x, y, MV_DOWN);
7855         started_moving = TRUE;
7856
7857         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7858         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7859       }
7860       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7861       {
7862         if (!MovDelay[x][y])
7863           MovDelay[x][y] = TILEY / 4 + 1;
7864
7865         if (MovDelay[x][y])
7866         {
7867           MovDelay[x][y]--;
7868           if (MovDelay[x][y])
7869             return;
7870         }
7871
7872         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7873         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7874         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7875         Store[x][y] = 0;
7876       }
7877     }
7878     else if ((CAN_PASS_MAGIC_WALL(element) &&
7879               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7880                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7881              (CAN_PASS_DC_MAGIC_WALL(element) &&
7882               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7883
7884     {
7885       InitMovingField(x, y, MV_DOWN);
7886       started_moving = TRUE;
7887
7888       Tile[x][y] =
7889         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7890          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7891          EL_DC_MAGIC_WALL_FILLING);
7892       Store[x][y] = element;
7893     }
7894     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
7895     {
7896       SplashAcid(x, y + 1);
7897
7898       InitMovingField(x, y, MV_DOWN);
7899       started_moving = TRUE;
7900
7901       Store[x][y] = EL_ACID;
7902     }
7903     else if (
7904              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7905               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7906              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7907               CAN_FALL(element) && WasJustFalling[x][y] &&
7908               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7909
7910              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7911               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7912               (Tile[x][y + 1] == EL_BLOCKED)))
7913     {
7914       /* this is needed for a special case not covered by calling "Impact()"
7915          from "ContinueMoving()": if an element moves to a tile directly below
7916          another element which was just falling on that tile (which was empty
7917          in the previous frame), the falling element above would just stop
7918          instead of smashing the element below (in previous version, the above
7919          element was just checked for "moving" instead of "falling", resulting
7920          in incorrect smashes caused by horizontal movement of the above
7921          element; also, the case of the player being the element to smash was
7922          simply not covered here... :-/ ) */
7923
7924       CheckCollision[x][y] = 0;
7925       CheckImpact[x][y] = 0;
7926
7927       Impact(x, y);
7928     }
7929     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7930     {
7931       if (MovDir[x][y] == MV_NONE)
7932       {
7933         InitMovingField(x, y, MV_DOWN);
7934         started_moving = TRUE;
7935       }
7936     }
7937     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
7938     {
7939       if (WasJustFalling[x][y]) // prevent animation from being restarted
7940         MovDir[x][y] = MV_DOWN;
7941
7942       InitMovingField(x, y, MV_DOWN);
7943       started_moving = TRUE;
7944     }
7945     else if (element == EL_AMOEBA_DROP)
7946     {
7947       Tile[x][y] = EL_AMOEBA_GROWING;
7948       Store[x][y] = EL_AMOEBA_WET;
7949     }
7950     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7951               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
7952              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7953              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7954     {
7955       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7956                                 (IS_FREE(x - 1, y + 1) ||
7957                                  Tile[x - 1][y + 1] == EL_ACID));
7958       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7959                                 (IS_FREE(x + 1, y + 1) ||
7960                                  Tile[x + 1][y + 1] == EL_ACID));
7961       boolean can_fall_any  = (can_fall_left || can_fall_right);
7962       boolean can_fall_both = (can_fall_left && can_fall_right);
7963       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
7964
7965       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7966       {
7967         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7968           can_fall_right = FALSE;
7969         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7970           can_fall_left = FALSE;
7971         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7972           can_fall_right = FALSE;
7973         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7974           can_fall_left = FALSE;
7975
7976         can_fall_any  = (can_fall_left || can_fall_right);
7977         can_fall_both = FALSE;
7978       }
7979
7980       if (can_fall_both)
7981       {
7982         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7983           can_fall_right = FALSE;       // slip down on left side
7984         else
7985           can_fall_left = !(can_fall_right = RND(2));
7986
7987         can_fall_both = FALSE;
7988       }
7989
7990       if (can_fall_any)
7991       {
7992         // if not determined otherwise, prefer left side for slipping down
7993         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7994         started_moving = TRUE;
7995       }
7996     }
7997     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
7998     {
7999       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8000       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8001       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8002       int belt_dir = game.belt_dir[belt_nr];
8003
8004       if ((belt_dir == MV_LEFT  && left_is_free) ||
8005           (belt_dir == MV_RIGHT && right_is_free))
8006       {
8007         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8008
8009         InitMovingField(x, y, belt_dir);
8010         started_moving = TRUE;
8011
8012         Pushed[x][y] = TRUE;
8013         Pushed[nextx][y] = TRUE;
8014
8015         GfxAction[x][y] = ACTION_DEFAULT;
8016       }
8017       else
8018       {
8019         MovDir[x][y] = 0;       // if element was moving, stop it
8020       }
8021     }
8022   }
8023
8024   // not "else if" because of elements that can fall and move (EL_SPRING)
8025   if (CAN_MOVE(element) && !started_moving)
8026   {
8027     int move_pattern = element_info[element].move_pattern;
8028     int newx, newy;
8029
8030     Moving2Blocked(x, y, &newx, &newy);
8031
8032     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8033       return;
8034
8035     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8036         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8037     {
8038       WasJustMoving[x][y] = 0;
8039       CheckCollision[x][y] = 0;
8040
8041       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8042
8043       if (Tile[x][y] != element)        // element has changed
8044         return;
8045     }
8046
8047     if (!MovDelay[x][y])        // start new movement phase
8048     {
8049       // all objects that can change their move direction after each step
8050       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8051
8052       if (element != EL_YAMYAM &&
8053           element != EL_DARK_YAMYAM &&
8054           element != EL_PACMAN &&
8055           !(move_pattern & MV_ANY_DIRECTION) &&
8056           move_pattern != MV_TURNING_LEFT &&
8057           move_pattern != MV_TURNING_RIGHT &&
8058           move_pattern != MV_TURNING_LEFT_RIGHT &&
8059           move_pattern != MV_TURNING_RIGHT_LEFT &&
8060           move_pattern != MV_TURNING_RANDOM)
8061       {
8062         TurnRound(x, y);
8063
8064         if (MovDelay[x][y] && (element == EL_BUG ||
8065                                element == EL_SPACESHIP ||
8066                                element == EL_SP_SNIKSNAK ||
8067                                element == EL_SP_ELECTRON ||
8068                                element == EL_MOLE))
8069           TEST_DrawLevelField(x, y);
8070       }
8071     }
8072
8073     if (MovDelay[x][y])         // wait some time before next movement
8074     {
8075       MovDelay[x][y]--;
8076
8077       if (element == EL_ROBOT ||
8078           element == EL_YAMYAM ||
8079           element == EL_DARK_YAMYAM)
8080       {
8081         DrawLevelElementAnimationIfNeeded(x, y, element);
8082         PlayLevelSoundAction(x, y, ACTION_WAITING);
8083       }
8084       else if (element == EL_SP_ELECTRON)
8085         DrawLevelElementAnimationIfNeeded(x, y, element);
8086       else if (element == EL_DRAGON)
8087       {
8088         int i;
8089         int dir = MovDir[x][y];
8090         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8091         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8092         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8093                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8094                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8095                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8096         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8097
8098         GfxAction[x][y] = ACTION_ATTACKING;
8099
8100         if (IS_PLAYER(x, y))
8101           DrawPlayerField(x, y);
8102         else
8103           TEST_DrawLevelField(x, y);
8104
8105         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8106
8107         for (i = 1; i <= 3; i++)
8108         {
8109           int xx = x + i * dx;
8110           int yy = y + i * dy;
8111           int sx = SCREENX(xx);
8112           int sy = SCREENY(yy);
8113           int flame_graphic = graphic + (i - 1);
8114
8115           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8116             break;
8117
8118           if (MovDelay[x][y])
8119           {
8120             int flamed = MovingOrBlocked2Element(xx, yy);
8121
8122             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8123               Bang(xx, yy);
8124             else
8125               RemoveMovingField(xx, yy);
8126
8127             ChangeDelay[xx][yy] = 0;
8128
8129             Tile[xx][yy] = EL_FLAMES;
8130
8131             if (IN_SCR_FIELD(sx, sy))
8132             {
8133               TEST_DrawLevelFieldCrumbled(xx, yy);
8134               DrawGraphic(sx, sy, flame_graphic, frame);
8135             }
8136           }
8137           else
8138           {
8139             if (Tile[xx][yy] == EL_FLAMES)
8140               Tile[xx][yy] = EL_EMPTY;
8141             TEST_DrawLevelField(xx, yy);
8142           }
8143         }
8144       }
8145
8146       if (MovDelay[x][y])       // element still has to wait some time
8147       {
8148         PlayLevelSoundAction(x, y, ACTION_WAITING);
8149
8150         return;
8151       }
8152     }
8153
8154     // now make next step
8155
8156     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8157
8158     if (DONT_COLLIDE_WITH(element) &&
8159         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8160         !PLAYER_ENEMY_PROTECTED(newx, newy))
8161     {
8162       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8163
8164       return;
8165     }
8166
8167     else if (CAN_MOVE_INTO_ACID(element) &&
8168              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8169              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8170              (MovDir[x][y] == MV_DOWN ||
8171               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8172     {
8173       SplashAcid(newx, newy);
8174       Store[x][y] = EL_ACID;
8175     }
8176     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8177     {
8178       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8179           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8180           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8181           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8182       {
8183         RemoveField(x, y);
8184         TEST_DrawLevelField(x, y);
8185
8186         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8187         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8188           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8189
8190         game.friends_still_needed--;
8191         if (!game.friends_still_needed &&
8192             !game.GameOver &&
8193             game.all_players_gone)
8194           LevelSolved();
8195
8196         return;
8197       }
8198       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8199       {
8200         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8201           TEST_DrawLevelField(newx, newy);
8202         else
8203           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8204       }
8205       else if (!IS_FREE(newx, newy))
8206       {
8207         GfxAction[x][y] = ACTION_WAITING;
8208
8209         if (IS_PLAYER(x, y))
8210           DrawPlayerField(x, y);
8211         else
8212           TEST_DrawLevelField(x, y);
8213
8214         return;
8215       }
8216     }
8217     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8218     {
8219       if (IS_FOOD_PIG(Tile[newx][newy]))
8220       {
8221         if (IS_MOVING(newx, newy))
8222           RemoveMovingField(newx, newy);
8223         else
8224         {
8225           Tile[newx][newy] = EL_EMPTY;
8226           TEST_DrawLevelField(newx, newy);
8227         }
8228
8229         PlayLevelSound(x, y, SND_PIG_DIGGING);
8230       }
8231       else if (!IS_FREE(newx, newy))
8232       {
8233         if (IS_PLAYER(x, y))
8234           DrawPlayerField(x, y);
8235         else
8236           TEST_DrawLevelField(x, y);
8237
8238         return;
8239       }
8240     }
8241     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8242     {
8243       if (Store[x][y] != EL_EMPTY)
8244       {
8245         boolean can_clone = FALSE;
8246         int xx, yy;
8247
8248         // check if element to clone is still there
8249         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8250         {
8251           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8252           {
8253             can_clone = TRUE;
8254
8255             break;
8256           }
8257         }
8258
8259         // cannot clone or target field not free anymore -- do not clone
8260         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8261           Store[x][y] = EL_EMPTY;
8262       }
8263
8264       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8265       {
8266         if (IS_MV_DIAGONAL(MovDir[x][y]))
8267         {
8268           int diagonal_move_dir = MovDir[x][y];
8269           int stored = Store[x][y];
8270           int change_delay = 8;
8271           int graphic;
8272
8273           // android is moving diagonally
8274
8275           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8276
8277           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8278           GfxElement[x][y] = EL_EMC_ANDROID;
8279           GfxAction[x][y] = ACTION_SHRINKING;
8280           GfxDir[x][y] = diagonal_move_dir;
8281           ChangeDelay[x][y] = change_delay;
8282
8283           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8284                                    GfxDir[x][y]);
8285
8286           DrawLevelGraphicAnimation(x, y, graphic);
8287           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8288
8289           if (Tile[newx][newy] == EL_ACID)
8290           {
8291             SplashAcid(newx, newy);
8292
8293             return;
8294           }
8295
8296           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8297
8298           Store[newx][newy] = EL_EMC_ANDROID;
8299           GfxElement[newx][newy] = EL_EMC_ANDROID;
8300           GfxAction[newx][newy] = ACTION_GROWING;
8301           GfxDir[newx][newy] = diagonal_move_dir;
8302           ChangeDelay[newx][newy] = change_delay;
8303
8304           graphic = el_act_dir2img(GfxElement[newx][newy],
8305                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8306
8307           DrawLevelGraphicAnimation(newx, newy, graphic);
8308           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8309
8310           return;
8311         }
8312         else
8313         {
8314           Tile[newx][newy] = EL_EMPTY;
8315           TEST_DrawLevelField(newx, newy);
8316
8317           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8318         }
8319       }
8320       else if (!IS_FREE(newx, newy))
8321       {
8322         return;
8323       }
8324     }
8325     else if (IS_CUSTOM_ELEMENT(element) &&
8326              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8327     {
8328       if (!DigFieldByCE(newx, newy, element))
8329         return;
8330
8331       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8332       {
8333         RunnerVisit[x][y] = FrameCounter;
8334         PlayerVisit[x][y] /= 8;         // expire player visit path
8335       }
8336     }
8337     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8338     {
8339       if (!IS_FREE(newx, newy))
8340       {
8341         if (IS_PLAYER(x, y))
8342           DrawPlayerField(x, y);
8343         else
8344           TEST_DrawLevelField(x, y);
8345
8346         return;
8347       }
8348       else
8349       {
8350         boolean wanna_flame = !RND(10);
8351         int dx = newx - x, dy = newy - y;
8352         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8353         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8354         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8355                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8356         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8357                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8358
8359         if ((wanna_flame ||
8360              IS_CLASSIC_ENEMY(element1) ||
8361              IS_CLASSIC_ENEMY(element2)) &&
8362             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8363             element1 != EL_FLAMES && element2 != EL_FLAMES)
8364         {
8365           ResetGfxAnimation(x, y);
8366           GfxAction[x][y] = ACTION_ATTACKING;
8367
8368           if (IS_PLAYER(x, y))
8369             DrawPlayerField(x, y);
8370           else
8371             TEST_DrawLevelField(x, y);
8372
8373           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8374
8375           MovDelay[x][y] = 50;
8376
8377           Tile[newx][newy] = EL_FLAMES;
8378           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8379             Tile[newx1][newy1] = EL_FLAMES;
8380           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8381             Tile[newx2][newy2] = EL_FLAMES;
8382
8383           return;
8384         }
8385       }
8386     }
8387     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8388              Tile[newx][newy] == EL_DIAMOND)
8389     {
8390       if (IS_MOVING(newx, newy))
8391         RemoveMovingField(newx, newy);
8392       else
8393       {
8394         Tile[newx][newy] = EL_EMPTY;
8395         TEST_DrawLevelField(newx, newy);
8396       }
8397
8398       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8399     }
8400     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8401              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8402     {
8403       if (AmoebaNr[newx][newy])
8404       {
8405         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8406         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8407             Tile[newx][newy] == EL_BD_AMOEBA)
8408           AmoebaCnt[AmoebaNr[newx][newy]]--;
8409       }
8410
8411       if (IS_MOVING(newx, newy))
8412       {
8413         RemoveMovingField(newx, newy);
8414       }
8415       else
8416       {
8417         Tile[newx][newy] = EL_EMPTY;
8418         TEST_DrawLevelField(newx, newy);
8419       }
8420
8421       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8422     }
8423     else if ((element == EL_PACMAN || element == EL_MOLE)
8424              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8425     {
8426       if (AmoebaNr[newx][newy])
8427       {
8428         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8429         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8430             Tile[newx][newy] == EL_BD_AMOEBA)
8431           AmoebaCnt[AmoebaNr[newx][newy]]--;
8432       }
8433
8434       if (element == EL_MOLE)
8435       {
8436         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8437         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8438
8439         ResetGfxAnimation(x, y);
8440         GfxAction[x][y] = ACTION_DIGGING;
8441         TEST_DrawLevelField(x, y);
8442
8443         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8444
8445         return;                         // wait for shrinking amoeba
8446       }
8447       else      // element == EL_PACMAN
8448       {
8449         Tile[newx][newy] = EL_EMPTY;
8450         TEST_DrawLevelField(newx, newy);
8451         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8452       }
8453     }
8454     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8455              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8456               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8457     {
8458       // wait for shrinking amoeba to completely disappear
8459       return;
8460     }
8461     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8462     {
8463       // object was running against a wall
8464
8465       TurnRound(x, y);
8466
8467       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8468         DrawLevelElementAnimation(x, y, element);
8469
8470       if (DONT_TOUCH(element))
8471         TestIfBadThingTouchesPlayer(x, y);
8472
8473       return;
8474     }
8475
8476     InitMovingField(x, y, MovDir[x][y]);
8477
8478     PlayLevelSoundAction(x, y, ACTION_MOVING);
8479   }
8480
8481   if (MovDir[x][y])
8482     ContinueMoving(x, y);
8483 }
8484
8485 void ContinueMoving(int x, int y)
8486 {
8487   int element = Tile[x][y];
8488   struct ElementInfo *ei = &element_info[element];
8489   int direction = MovDir[x][y];
8490   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8491   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8492   int newx = x + dx, newy = y + dy;
8493   int stored = Store[x][y];
8494   int stored_new = Store[newx][newy];
8495   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8496   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8497   boolean last_line = (newy == lev_fieldy - 1);
8498
8499   MovPos[x][y] += getElementMoveStepsize(x, y);
8500
8501   if (pushed_by_player) // special case: moving object pushed by player
8502     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8503
8504   if (ABS(MovPos[x][y]) < TILEX)
8505   {
8506     TEST_DrawLevelField(x, y);
8507
8508     return;     // element is still moving
8509   }
8510
8511   // element reached destination field
8512
8513   Tile[x][y] = EL_EMPTY;
8514   Tile[newx][newy] = element;
8515   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8516
8517   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8518   {
8519     element = Tile[newx][newy] = EL_ACID;
8520   }
8521   else if (element == EL_MOLE)
8522   {
8523     Tile[x][y] = EL_SAND;
8524
8525     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8526   }
8527   else if (element == EL_QUICKSAND_FILLING)
8528   {
8529     element = Tile[newx][newy] = get_next_element(element);
8530     Store[newx][newy] = Store[x][y];
8531   }
8532   else if (element == EL_QUICKSAND_EMPTYING)
8533   {
8534     Tile[x][y] = get_next_element(element);
8535     element = Tile[newx][newy] = Store[x][y];
8536   }
8537   else if (element == EL_QUICKSAND_FAST_FILLING)
8538   {
8539     element = Tile[newx][newy] = get_next_element(element);
8540     Store[newx][newy] = Store[x][y];
8541   }
8542   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8543   {
8544     Tile[x][y] = get_next_element(element);
8545     element = Tile[newx][newy] = Store[x][y];
8546   }
8547   else if (element == EL_MAGIC_WALL_FILLING)
8548   {
8549     element = Tile[newx][newy] = get_next_element(element);
8550     if (!game.magic_wall_active)
8551       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8552     Store[newx][newy] = Store[x][y];
8553   }
8554   else if (element == EL_MAGIC_WALL_EMPTYING)
8555   {
8556     Tile[x][y] = get_next_element(element);
8557     if (!game.magic_wall_active)
8558       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8559     element = Tile[newx][newy] = Store[x][y];
8560
8561     InitField(newx, newy, FALSE);
8562   }
8563   else if (element == EL_BD_MAGIC_WALL_FILLING)
8564   {
8565     element = Tile[newx][newy] = get_next_element(element);
8566     if (!game.magic_wall_active)
8567       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8568     Store[newx][newy] = Store[x][y];
8569   }
8570   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8571   {
8572     Tile[x][y] = get_next_element(element);
8573     if (!game.magic_wall_active)
8574       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8575     element = Tile[newx][newy] = Store[x][y];
8576
8577     InitField(newx, newy, FALSE);
8578   }
8579   else if (element == EL_DC_MAGIC_WALL_FILLING)
8580   {
8581     element = Tile[newx][newy] = get_next_element(element);
8582     if (!game.magic_wall_active)
8583       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8584     Store[newx][newy] = Store[x][y];
8585   }
8586   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8587   {
8588     Tile[x][y] = get_next_element(element);
8589     if (!game.magic_wall_active)
8590       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8591     element = Tile[newx][newy] = Store[x][y];
8592
8593     InitField(newx, newy, FALSE);
8594   }
8595   else if (element == EL_AMOEBA_DROPPING)
8596   {
8597     Tile[x][y] = get_next_element(element);
8598     element = Tile[newx][newy] = Store[x][y];
8599   }
8600   else if (element == EL_SOKOBAN_OBJECT)
8601   {
8602     if (Back[x][y])
8603       Tile[x][y] = Back[x][y];
8604
8605     if (Back[newx][newy])
8606       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8607
8608     Back[x][y] = Back[newx][newy] = 0;
8609   }
8610
8611   Store[x][y] = EL_EMPTY;
8612   MovPos[x][y] = 0;
8613   MovDir[x][y] = 0;
8614   MovDelay[x][y] = 0;
8615
8616   MovDelay[newx][newy] = 0;
8617
8618   if (CAN_CHANGE_OR_HAS_ACTION(element))
8619   {
8620     // copy element change control values to new field
8621     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8622     ChangePage[newx][newy]  = ChangePage[x][y];
8623     ChangeCount[newx][newy] = ChangeCount[x][y];
8624     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8625   }
8626
8627   CustomValue[newx][newy] = CustomValue[x][y];
8628
8629   ChangeDelay[x][y] = 0;
8630   ChangePage[x][y] = -1;
8631   ChangeCount[x][y] = 0;
8632   ChangeEvent[x][y] = -1;
8633
8634   CustomValue[x][y] = 0;
8635
8636   // copy animation control values to new field
8637   GfxFrame[newx][newy]  = GfxFrame[x][y];
8638   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8639   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8640   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8641
8642   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8643
8644   // some elements can leave other elements behind after moving
8645   if (ei->move_leave_element != EL_EMPTY &&
8646       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8647       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8648   {
8649     int move_leave_element = ei->move_leave_element;
8650
8651     // this makes it possible to leave the removed element again
8652     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8653       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8654
8655     Tile[x][y] = move_leave_element;
8656
8657     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8658       MovDir[x][y] = direction;
8659
8660     InitField(x, y, FALSE);
8661
8662     if (GFX_CRUMBLED(Tile[x][y]))
8663       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8664
8665     if (ELEM_IS_PLAYER(move_leave_element))
8666       RelocatePlayer(x, y, move_leave_element);
8667   }
8668
8669   // do this after checking for left-behind element
8670   ResetGfxAnimation(x, y);      // reset animation values for old field
8671
8672   if (!CAN_MOVE(element) ||
8673       (CAN_FALL(element) && direction == MV_DOWN &&
8674        (element == EL_SPRING ||
8675         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8676         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8677     GfxDir[x][y] = MovDir[newx][newy] = 0;
8678
8679   TEST_DrawLevelField(x, y);
8680   TEST_DrawLevelField(newx, newy);
8681
8682   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8683
8684   // prevent pushed element from moving on in pushed direction
8685   if (pushed_by_player && CAN_MOVE(element) &&
8686       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8687       !(element_info[element].move_pattern & direction))
8688     TurnRound(newx, newy);
8689
8690   // prevent elements on conveyor belt from moving on in last direction
8691   if (pushed_by_conveyor && CAN_FALL(element) &&
8692       direction & MV_HORIZONTAL)
8693     MovDir[newx][newy] = 0;
8694
8695   if (!pushed_by_player)
8696   {
8697     int nextx = newx + dx, nexty = newy + dy;
8698     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8699
8700     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8701
8702     if (CAN_FALL(element) && direction == MV_DOWN)
8703       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8704
8705     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8706       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8707
8708     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8709       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8710   }
8711
8712   if (DONT_TOUCH(element))      // object may be nasty to player or others
8713   {
8714     TestIfBadThingTouchesPlayer(newx, newy);
8715     TestIfBadThingTouchesFriend(newx, newy);
8716
8717     if (!IS_CUSTOM_ELEMENT(element))
8718       TestIfBadThingTouchesOtherBadThing(newx, newy);
8719   }
8720   else if (element == EL_PENGUIN)
8721     TestIfFriendTouchesBadThing(newx, newy);
8722
8723   if (DONT_GET_HIT_BY(element))
8724   {
8725     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8726   }
8727
8728   // give the player one last chance (one more frame) to move away
8729   if (CAN_FALL(element) && direction == MV_DOWN &&
8730       (last_line || (!IS_FREE(x, newy + 1) &&
8731                      (!IS_PLAYER(x, newy + 1) ||
8732                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8733     Impact(x, newy);
8734
8735   if (pushed_by_player && !game.use_change_when_pushing_bug)
8736   {
8737     int push_side = MV_DIR_OPPOSITE(direction);
8738     struct PlayerInfo *player = PLAYERINFO(x, y);
8739
8740     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8741                                player->index_bit, push_side);
8742     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8743                                         player->index_bit, push_side);
8744   }
8745
8746   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8747     MovDelay[newx][newy] = 1;
8748
8749   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8750
8751   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8752   TestIfElementHitsCustomElement(newx, newy, direction);
8753   TestIfPlayerTouchesCustomElement(newx, newy);
8754   TestIfElementTouchesCustomElement(newx, newy);
8755
8756   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8757       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8758     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8759                              MV_DIR_OPPOSITE(direction));
8760 }
8761
8762 int AmoebaNeighbourNr(int ax, int ay)
8763 {
8764   int i;
8765   int element = Tile[ax][ay];
8766   int group_nr = 0;
8767   static int xy[4][2] =
8768   {
8769     { 0, -1 },
8770     { -1, 0 },
8771     { +1, 0 },
8772     { 0, +1 }
8773   };
8774
8775   for (i = 0; i < NUM_DIRECTIONS; i++)
8776   {
8777     int x = ax + xy[i][0];
8778     int y = ay + xy[i][1];
8779
8780     if (!IN_LEV_FIELD(x, y))
8781       continue;
8782
8783     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8784       group_nr = AmoebaNr[x][y];
8785   }
8786
8787   return group_nr;
8788 }
8789
8790 static void AmoebaMerge(int ax, int ay)
8791 {
8792   int i, x, y, xx, yy;
8793   int new_group_nr = AmoebaNr[ax][ay];
8794   static int xy[4][2] =
8795   {
8796     { 0, -1 },
8797     { -1, 0 },
8798     { +1, 0 },
8799     { 0, +1 }
8800   };
8801
8802   if (new_group_nr == 0)
8803     return;
8804
8805   for (i = 0; i < NUM_DIRECTIONS; i++)
8806   {
8807     x = ax + xy[i][0];
8808     y = ay + xy[i][1];
8809
8810     if (!IN_LEV_FIELD(x, y))
8811       continue;
8812
8813     if ((Tile[x][y] == EL_AMOEBA_FULL ||
8814          Tile[x][y] == EL_BD_AMOEBA ||
8815          Tile[x][y] == EL_AMOEBA_DEAD) &&
8816         AmoebaNr[x][y] != new_group_nr)
8817     {
8818       int old_group_nr = AmoebaNr[x][y];
8819
8820       if (old_group_nr == 0)
8821         return;
8822
8823       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8824       AmoebaCnt[old_group_nr] = 0;
8825       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8826       AmoebaCnt2[old_group_nr] = 0;
8827
8828       SCAN_PLAYFIELD(xx, yy)
8829       {
8830         if (AmoebaNr[xx][yy] == old_group_nr)
8831           AmoebaNr[xx][yy] = new_group_nr;
8832       }
8833     }
8834   }
8835 }
8836
8837 void AmoebaToDiamond(int ax, int ay)
8838 {
8839   int i, x, y;
8840
8841   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
8842   {
8843     int group_nr = AmoebaNr[ax][ay];
8844
8845 #ifdef DEBUG
8846     if (group_nr == 0)
8847     {
8848       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
8849       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
8850
8851       return;
8852     }
8853 #endif
8854
8855     SCAN_PLAYFIELD(x, y)
8856     {
8857       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8858       {
8859         AmoebaNr[x][y] = 0;
8860         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
8861       }
8862     }
8863
8864     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8865                             SND_AMOEBA_TURNING_TO_GEM :
8866                             SND_AMOEBA_TURNING_TO_ROCK));
8867     Bang(ax, ay);
8868   }
8869   else
8870   {
8871     static int xy[4][2] =
8872     {
8873       { 0, -1 },
8874       { -1, 0 },
8875       { +1, 0 },
8876       { 0, +1 }
8877     };
8878
8879     for (i = 0; i < NUM_DIRECTIONS; i++)
8880     {
8881       x = ax + xy[i][0];
8882       y = ay + xy[i][1];
8883
8884       if (!IN_LEV_FIELD(x, y))
8885         continue;
8886
8887       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
8888       {
8889         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8890                               SND_AMOEBA_TURNING_TO_GEM :
8891                               SND_AMOEBA_TURNING_TO_ROCK));
8892         Bang(x, y);
8893       }
8894     }
8895   }
8896 }
8897
8898 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
8899 {
8900   int x, y;
8901   int group_nr = AmoebaNr[ax][ay];
8902   boolean done = FALSE;
8903
8904 #ifdef DEBUG
8905   if (group_nr == 0)
8906   {
8907     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
8908     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
8909
8910     return;
8911   }
8912 #endif
8913
8914   SCAN_PLAYFIELD(x, y)
8915   {
8916     if (AmoebaNr[x][y] == group_nr &&
8917         (Tile[x][y] == EL_AMOEBA_DEAD ||
8918          Tile[x][y] == EL_BD_AMOEBA ||
8919          Tile[x][y] == EL_AMOEBA_GROWING))
8920     {
8921       AmoebaNr[x][y] = 0;
8922       Tile[x][y] = new_element;
8923       InitField(x, y, FALSE);
8924       TEST_DrawLevelField(x, y);
8925       done = TRUE;
8926     }
8927   }
8928
8929   if (done)
8930     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8931                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8932                             SND_BD_AMOEBA_TURNING_TO_GEM));
8933 }
8934
8935 static void AmoebaGrowing(int x, int y)
8936 {
8937   static unsigned int sound_delay = 0;
8938   static unsigned int sound_delay_value = 0;
8939
8940   if (!MovDelay[x][y])          // start new growing cycle
8941   {
8942     MovDelay[x][y] = 7;
8943
8944     if (DelayReached(&sound_delay, sound_delay_value))
8945     {
8946       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8947       sound_delay_value = 30;
8948     }
8949   }
8950
8951   if (MovDelay[x][y])           // wait some time before growing bigger
8952   {
8953     MovDelay[x][y]--;
8954     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8955     {
8956       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8957                                            6 - MovDelay[x][y]);
8958
8959       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8960     }
8961
8962     if (!MovDelay[x][y])
8963     {
8964       Tile[x][y] = Store[x][y];
8965       Store[x][y] = 0;
8966       TEST_DrawLevelField(x, y);
8967     }
8968   }
8969 }
8970
8971 static void AmoebaShrinking(int x, int y)
8972 {
8973   static unsigned int sound_delay = 0;
8974   static unsigned int sound_delay_value = 0;
8975
8976   if (!MovDelay[x][y])          // start new shrinking cycle
8977   {
8978     MovDelay[x][y] = 7;
8979
8980     if (DelayReached(&sound_delay, sound_delay_value))
8981       sound_delay_value = 30;
8982   }
8983
8984   if (MovDelay[x][y])           // wait some time before shrinking
8985   {
8986     MovDelay[x][y]--;
8987     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8988     {
8989       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8990                                            6 - MovDelay[x][y]);
8991
8992       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8993     }
8994
8995     if (!MovDelay[x][y])
8996     {
8997       Tile[x][y] = EL_EMPTY;
8998       TEST_DrawLevelField(x, y);
8999
9000       // don't let mole enter this field in this cycle;
9001       // (give priority to objects falling to this field from above)
9002       Stop[x][y] = TRUE;
9003     }
9004   }
9005 }
9006
9007 static void AmoebaReproduce(int ax, int ay)
9008 {
9009   int i;
9010   int element = Tile[ax][ay];
9011   int graphic = el2img(element);
9012   int newax = ax, neway = ay;
9013   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9014   static int xy[4][2] =
9015   {
9016     { 0, -1 },
9017     { -1, 0 },
9018     { +1, 0 },
9019     { 0, +1 }
9020   };
9021
9022   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9023   {
9024     Tile[ax][ay] = EL_AMOEBA_DEAD;
9025     TEST_DrawLevelField(ax, ay);
9026     return;
9027   }
9028
9029   if (IS_ANIMATED(graphic))
9030     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9031
9032   if (!MovDelay[ax][ay])        // start making new amoeba field
9033     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9034
9035   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9036   {
9037     MovDelay[ax][ay]--;
9038     if (MovDelay[ax][ay])
9039       return;
9040   }
9041
9042   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9043   {
9044     int start = RND(4);
9045     int x = ax + xy[start][0];
9046     int y = ay + xy[start][1];
9047
9048     if (!IN_LEV_FIELD(x, y))
9049       return;
9050
9051     if (IS_FREE(x, y) ||
9052         CAN_GROW_INTO(Tile[x][y]) ||
9053         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9054         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9055     {
9056       newax = x;
9057       neway = y;
9058     }
9059
9060     if (newax == ax && neway == ay)
9061       return;
9062   }
9063   else                          // normal or "filled" (BD style) amoeba
9064   {
9065     int start = RND(4);
9066     boolean waiting_for_player = FALSE;
9067
9068     for (i = 0; i < NUM_DIRECTIONS; i++)
9069     {
9070       int j = (start + i) % 4;
9071       int x = ax + xy[j][0];
9072       int y = ay + xy[j][1];
9073
9074       if (!IN_LEV_FIELD(x, y))
9075         continue;
9076
9077       if (IS_FREE(x, y) ||
9078           CAN_GROW_INTO(Tile[x][y]) ||
9079           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9080           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9081       {
9082         newax = x;
9083         neway = y;
9084         break;
9085       }
9086       else if (IS_PLAYER(x, y))
9087         waiting_for_player = TRUE;
9088     }
9089
9090     if (newax == ax && neway == ay)             // amoeba cannot grow
9091     {
9092       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9093       {
9094         Tile[ax][ay] = EL_AMOEBA_DEAD;
9095         TEST_DrawLevelField(ax, ay);
9096         AmoebaCnt[AmoebaNr[ax][ay]]--;
9097
9098         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9099         {
9100           if (element == EL_AMOEBA_FULL)
9101             AmoebaToDiamond(ax, ay);
9102           else if (element == EL_BD_AMOEBA)
9103             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9104         }
9105       }
9106       return;
9107     }
9108     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9109     {
9110       // amoeba gets larger by growing in some direction
9111
9112       int new_group_nr = AmoebaNr[ax][ay];
9113
9114 #ifdef DEBUG
9115   if (new_group_nr == 0)
9116   {
9117     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9118           newax, neway);
9119     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9120
9121     return;
9122   }
9123 #endif
9124
9125       AmoebaNr[newax][neway] = new_group_nr;
9126       AmoebaCnt[new_group_nr]++;
9127       AmoebaCnt2[new_group_nr]++;
9128
9129       // if amoeba touches other amoeba(s) after growing, unify them
9130       AmoebaMerge(newax, neway);
9131
9132       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9133       {
9134         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9135         return;
9136       }
9137     }
9138   }
9139
9140   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9141       (neway == lev_fieldy - 1 && newax != ax))
9142   {
9143     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9144     Store[newax][neway] = element;
9145   }
9146   else if (neway == ay || element == EL_EMC_DRIPPER)
9147   {
9148     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9149
9150     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9151   }
9152   else
9153   {
9154     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9155     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9156     Store[ax][ay] = EL_AMOEBA_DROP;
9157     ContinueMoving(ax, ay);
9158     return;
9159   }
9160
9161   TEST_DrawLevelField(newax, neway);
9162 }
9163
9164 static void Life(int ax, int ay)
9165 {
9166   int x1, y1, x2, y2;
9167   int life_time = 40;
9168   int element = Tile[ax][ay];
9169   int graphic = el2img(element);
9170   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9171                          level.biomaze);
9172   boolean changed = FALSE;
9173
9174   if (IS_ANIMATED(graphic))
9175     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9176
9177   if (Stop[ax][ay])
9178     return;
9179
9180   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9181     MovDelay[ax][ay] = life_time;
9182
9183   if (MovDelay[ax][ay])         // wait some time before next cycle
9184   {
9185     MovDelay[ax][ay]--;
9186     if (MovDelay[ax][ay])
9187       return;
9188   }
9189
9190   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9191   {
9192     int xx = ax+x1, yy = ay+y1;
9193     int old_element = Tile[xx][yy];
9194     int num_neighbours = 0;
9195
9196     if (!IN_LEV_FIELD(xx, yy))
9197       continue;
9198
9199     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9200     {
9201       int x = xx+x2, y = yy+y2;
9202
9203       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9204         continue;
9205
9206       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9207       boolean is_neighbour = FALSE;
9208
9209       if (level.use_life_bugs)
9210         is_neighbour =
9211           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9212            (IS_FREE(x, y)                             &&  Stop[x][y]));
9213       else
9214         is_neighbour =
9215           (Last[x][y] == element || is_player_cell);
9216
9217       if (is_neighbour)
9218         num_neighbours++;
9219     }
9220
9221     boolean is_free = FALSE;
9222
9223     if (level.use_life_bugs)
9224       is_free = (IS_FREE(xx, yy));
9225     else
9226       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9227
9228     if (xx == ax && yy == ay)           // field in the middle
9229     {
9230       if (num_neighbours < life_parameter[0] ||
9231           num_neighbours > life_parameter[1])
9232       {
9233         Tile[xx][yy] = EL_EMPTY;
9234         if (Tile[xx][yy] != old_element)
9235           TEST_DrawLevelField(xx, yy);
9236         Stop[xx][yy] = TRUE;
9237         changed = TRUE;
9238       }
9239     }
9240     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9241     {                                   // free border field
9242       if (num_neighbours >= life_parameter[2] &&
9243           num_neighbours <= life_parameter[3])
9244       {
9245         Tile[xx][yy] = element;
9246         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9247         if (Tile[xx][yy] != old_element)
9248           TEST_DrawLevelField(xx, yy);
9249         Stop[xx][yy] = TRUE;
9250         changed = TRUE;
9251       }
9252     }
9253   }
9254
9255   if (changed)
9256     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9257                    SND_GAME_OF_LIFE_GROWING);
9258 }
9259
9260 static void InitRobotWheel(int x, int y)
9261 {
9262   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9263 }
9264
9265 static void RunRobotWheel(int x, int y)
9266 {
9267   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9268 }
9269
9270 static void StopRobotWheel(int x, int y)
9271 {
9272   if (game.robot_wheel_x == x &&
9273       game.robot_wheel_y == y)
9274   {
9275     game.robot_wheel_x = -1;
9276     game.robot_wheel_y = -1;
9277     game.robot_wheel_active = FALSE;
9278   }
9279 }
9280
9281 static void InitTimegateWheel(int x, int y)
9282 {
9283   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9284 }
9285
9286 static void RunTimegateWheel(int x, int y)
9287 {
9288   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9289 }
9290
9291 static void InitMagicBallDelay(int x, int y)
9292 {
9293   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9294 }
9295
9296 static void ActivateMagicBall(int bx, int by)
9297 {
9298   int x, y;
9299
9300   if (level.ball_random)
9301   {
9302     int pos_border = RND(8);    // select one of the eight border elements
9303     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9304     int xx = pos_content % 3;
9305     int yy = pos_content / 3;
9306
9307     x = bx - 1 + xx;
9308     y = by - 1 + yy;
9309
9310     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9311       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9312   }
9313   else
9314   {
9315     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9316     {
9317       int xx = x - bx + 1;
9318       int yy = y - by + 1;
9319
9320       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9321         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9322     }
9323   }
9324
9325   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9326 }
9327
9328 static void CheckExit(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_EXIT_OPENING;
9349
9350   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9351 }
9352
9353 static void CheckExitEM(int x, int y)
9354 {
9355   if (game.gems_still_needed > 0 ||
9356       game.sokoban_fields_still_needed > 0 ||
9357       game.sokoban_objects_still_needed > 0 ||
9358       game.lights_still_needed > 0)
9359   {
9360     int element = Tile[x][y];
9361     int graphic = el2img(element);
9362
9363     if (IS_ANIMATED(graphic))
9364       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9365
9366     return;
9367   }
9368
9369   // do not re-open exit door closed after last player
9370   if (game.all_players_gone)
9371     return;
9372
9373   Tile[x][y] = EL_EM_EXIT_OPENING;
9374
9375   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9376 }
9377
9378 static void CheckExitSteel(int x, int y)
9379 {
9380   if (game.gems_still_needed > 0 ||
9381       game.sokoban_fields_still_needed > 0 ||
9382       game.sokoban_objects_still_needed > 0 ||
9383       game.lights_still_needed > 0)
9384   {
9385     int element = Tile[x][y];
9386     int graphic = el2img(element);
9387
9388     if (IS_ANIMATED(graphic))
9389       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9390
9391     return;
9392   }
9393
9394   // do not re-open exit door closed after last player
9395   if (game.all_players_gone)
9396     return;
9397
9398   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9399
9400   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9401 }
9402
9403 static void CheckExitSteelEM(int x, int y)
9404 {
9405   if (game.gems_still_needed > 0 ||
9406       game.sokoban_fields_still_needed > 0 ||
9407       game.sokoban_objects_still_needed > 0 ||
9408       game.lights_still_needed > 0)
9409   {
9410     int element = Tile[x][y];
9411     int graphic = el2img(element);
9412
9413     if (IS_ANIMATED(graphic))
9414       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9415
9416     return;
9417   }
9418
9419   // do not re-open exit door closed after last player
9420   if (game.all_players_gone)
9421     return;
9422
9423   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9424
9425   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9426 }
9427
9428 static void CheckExitSP(int x, int y)
9429 {
9430   if (game.gems_still_needed > 0)
9431   {
9432     int element = Tile[x][y];
9433     int graphic = el2img(element);
9434
9435     if (IS_ANIMATED(graphic))
9436       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9437
9438     return;
9439   }
9440
9441   // do not re-open exit door closed after last player
9442   if (game.all_players_gone)
9443     return;
9444
9445   Tile[x][y] = EL_SP_EXIT_OPENING;
9446
9447   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9448 }
9449
9450 static void CloseAllOpenTimegates(void)
9451 {
9452   int x, y;
9453
9454   SCAN_PLAYFIELD(x, y)
9455   {
9456     int element = Tile[x][y];
9457
9458     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9459     {
9460       Tile[x][y] = EL_TIMEGATE_CLOSING;
9461
9462       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9463     }
9464   }
9465 }
9466
9467 static void DrawTwinkleOnField(int x, int y)
9468 {
9469   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9470     return;
9471
9472   if (Tile[x][y] == EL_BD_DIAMOND)
9473     return;
9474
9475   if (MovDelay[x][y] == 0)      // next animation frame
9476     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9477
9478   if (MovDelay[x][y] != 0)      // wait some time before next frame
9479   {
9480     MovDelay[x][y]--;
9481
9482     DrawLevelElementAnimation(x, y, Tile[x][y]);
9483
9484     if (MovDelay[x][y] != 0)
9485     {
9486       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9487                                            10 - MovDelay[x][y]);
9488
9489       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9490     }
9491   }
9492 }
9493
9494 static void MauerWaechst(int x, int y)
9495 {
9496   int delay = 6;
9497
9498   if (!MovDelay[x][y])          // next animation frame
9499     MovDelay[x][y] = 3 * delay;
9500
9501   if (MovDelay[x][y])           // wait some time before next frame
9502   {
9503     MovDelay[x][y]--;
9504
9505     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9506     {
9507       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9508       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9509
9510       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9511     }
9512
9513     if (!MovDelay[x][y])
9514     {
9515       if (MovDir[x][y] == MV_LEFT)
9516       {
9517         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9518           TEST_DrawLevelField(x - 1, y);
9519       }
9520       else if (MovDir[x][y] == MV_RIGHT)
9521       {
9522         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9523           TEST_DrawLevelField(x + 1, y);
9524       }
9525       else if (MovDir[x][y] == MV_UP)
9526       {
9527         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9528           TEST_DrawLevelField(x, y - 1);
9529       }
9530       else
9531       {
9532         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9533           TEST_DrawLevelField(x, y + 1);
9534       }
9535
9536       Tile[x][y] = Store[x][y];
9537       Store[x][y] = 0;
9538       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9539       TEST_DrawLevelField(x, y);
9540     }
9541   }
9542 }
9543
9544 static void MauerAbleger(int ax, int ay)
9545 {
9546   int element = Tile[ax][ay];
9547   int graphic = el2img(element);
9548   boolean oben_frei = FALSE, unten_frei = FALSE;
9549   boolean links_frei = FALSE, rechts_frei = FALSE;
9550   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9551   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9552   boolean new_wall = FALSE;
9553
9554   if (IS_ANIMATED(graphic))
9555     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9556
9557   if (!MovDelay[ax][ay])        // start building new wall
9558     MovDelay[ax][ay] = 6;
9559
9560   if (MovDelay[ax][ay])         // wait some time before building new wall
9561   {
9562     MovDelay[ax][ay]--;
9563     if (MovDelay[ax][ay])
9564       return;
9565   }
9566
9567   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9568     oben_frei = TRUE;
9569   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9570     unten_frei = TRUE;
9571   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9572     links_frei = TRUE;
9573   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9574     rechts_frei = TRUE;
9575
9576   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9577       element == EL_EXPANDABLE_WALL_ANY)
9578   {
9579     if (oben_frei)
9580     {
9581       Tile[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9582       Store[ax][ay-1] = element;
9583       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9584       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9585         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9586                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9587       new_wall = TRUE;
9588     }
9589     if (unten_frei)
9590     {
9591       Tile[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9592       Store[ax][ay+1] = element;
9593       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9594       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9595         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9596                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9597       new_wall = TRUE;
9598     }
9599   }
9600
9601   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9602       element == EL_EXPANDABLE_WALL_ANY ||
9603       element == EL_EXPANDABLE_WALL ||
9604       element == EL_BD_EXPANDABLE_WALL)
9605   {
9606     if (links_frei)
9607     {
9608       Tile[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9609       Store[ax-1][ay] = element;
9610       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9611       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9612         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9613                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9614       new_wall = TRUE;
9615     }
9616
9617     if (rechts_frei)
9618     {
9619       Tile[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9620       Store[ax+1][ay] = element;
9621       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9622       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9623         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9624                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9625       new_wall = TRUE;
9626     }
9627   }
9628
9629   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9630     TEST_DrawLevelField(ax, ay);
9631
9632   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9633     oben_massiv = TRUE;
9634   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9635     unten_massiv = TRUE;
9636   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9637     links_massiv = TRUE;
9638   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9639     rechts_massiv = TRUE;
9640
9641   if (((oben_massiv && unten_massiv) ||
9642        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9643        element == EL_EXPANDABLE_WALL) &&
9644       ((links_massiv && rechts_massiv) ||
9645        element == EL_EXPANDABLE_WALL_VERTICAL))
9646     Tile[ax][ay] = EL_WALL;
9647
9648   if (new_wall)
9649     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9650 }
9651
9652 static void MauerAblegerStahl(int ax, int ay)
9653 {
9654   int element = Tile[ax][ay];
9655   int graphic = el2img(element);
9656   boolean oben_frei = FALSE, unten_frei = FALSE;
9657   boolean links_frei = FALSE, rechts_frei = FALSE;
9658   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9659   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9660   boolean new_wall = FALSE;
9661
9662   if (IS_ANIMATED(graphic))
9663     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9664
9665   if (!MovDelay[ax][ay])        // start building new wall
9666     MovDelay[ax][ay] = 6;
9667
9668   if (MovDelay[ax][ay])         // wait some time before building new wall
9669   {
9670     MovDelay[ax][ay]--;
9671     if (MovDelay[ax][ay])
9672       return;
9673   }
9674
9675   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9676     oben_frei = TRUE;
9677   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9678     unten_frei = TRUE;
9679   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9680     links_frei = TRUE;
9681   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9682     rechts_frei = TRUE;
9683
9684   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9685       element == EL_EXPANDABLE_STEELWALL_ANY)
9686   {
9687     if (oben_frei)
9688     {
9689       Tile[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9690       Store[ax][ay-1] = element;
9691       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9692       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9693         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9694                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9695       new_wall = TRUE;
9696     }
9697     if (unten_frei)
9698     {
9699       Tile[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9700       Store[ax][ay+1] = element;
9701       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9702       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9703         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9704                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9705       new_wall = TRUE;
9706     }
9707   }
9708
9709   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9710       element == EL_EXPANDABLE_STEELWALL_ANY)
9711   {
9712     if (links_frei)
9713     {
9714       Tile[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9715       Store[ax-1][ay] = element;
9716       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9717       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9718         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9719                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9720       new_wall = TRUE;
9721     }
9722
9723     if (rechts_frei)
9724     {
9725       Tile[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9726       Store[ax+1][ay] = element;
9727       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9728       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9729         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9730                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9731       new_wall = TRUE;
9732     }
9733   }
9734
9735   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9736     oben_massiv = TRUE;
9737   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9738     unten_massiv = TRUE;
9739   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9740     links_massiv = TRUE;
9741   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9742     rechts_massiv = TRUE;
9743
9744   if (((oben_massiv && unten_massiv) ||
9745        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9746       ((links_massiv && rechts_massiv) ||
9747        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9748     Tile[ax][ay] = EL_STEELWALL;
9749
9750   if (new_wall)
9751     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9752 }
9753
9754 static void CheckForDragon(int x, int y)
9755 {
9756   int i, j;
9757   boolean dragon_found = FALSE;
9758   static int xy[4][2] =
9759   {
9760     { 0, -1 },
9761     { -1, 0 },
9762     { +1, 0 },
9763     { 0, +1 }
9764   };
9765
9766   for (i = 0; i < NUM_DIRECTIONS; i++)
9767   {
9768     for (j = 0; j < 4; j++)
9769     {
9770       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9771
9772       if (IN_LEV_FIELD(xx, yy) &&
9773           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9774       {
9775         if (Tile[xx][yy] == EL_DRAGON)
9776           dragon_found = TRUE;
9777       }
9778       else
9779         break;
9780     }
9781   }
9782
9783   if (!dragon_found)
9784   {
9785     for (i = 0; i < NUM_DIRECTIONS; i++)
9786     {
9787       for (j = 0; j < 3; j++)
9788       {
9789         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9790   
9791         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9792         {
9793           Tile[xx][yy] = EL_EMPTY;
9794           TEST_DrawLevelField(xx, yy);
9795         }
9796         else
9797           break;
9798       }
9799     }
9800   }
9801 }
9802
9803 static void InitBuggyBase(int x, int y)
9804 {
9805   int element = Tile[x][y];
9806   int activating_delay = FRAMES_PER_SECOND / 4;
9807
9808   ChangeDelay[x][y] =
9809     (element == EL_SP_BUGGY_BASE ?
9810      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9811      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9812      activating_delay :
9813      element == EL_SP_BUGGY_BASE_ACTIVE ?
9814      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9815 }
9816
9817 static void WarnBuggyBase(int x, int y)
9818 {
9819   int i;
9820   static int xy[4][2] =
9821   {
9822     { 0, -1 },
9823     { -1, 0 },
9824     { +1, 0 },
9825     { 0, +1 }
9826   };
9827
9828   for (i = 0; i < NUM_DIRECTIONS; i++)
9829   {
9830     int xx = x + xy[i][0];
9831     int yy = y + xy[i][1];
9832
9833     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9834     {
9835       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9836
9837       break;
9838     }
9839   }
9840 }
9841
9842 static void InitTrap(int x, int y)
9843 {
9844   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9845 }
9846
9847 static void ActivateTrap(int x, int y)
9848 {
9849   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9850 }
9851
9852 static void ChangeActiveTrap(int x, int y)
9853 {
9854   int graphic = IMG_TRAP_ACTIVE;
9855
9856   // if new animation frame was drawn, correct crumbled sand border
9857   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9858     TEST_DrawLevelFieldCrumbled(x, y);
9859 }
9860
9861 static int getSpecialActionElement(int element, int number, int base_element)
9862 {
9863   return (element != EL_EMPTY ? element :
9864           number != -1 ? base_element + number - 1 :
9865           EL_EMPTY);
9866 }
9867
9868 static int getModifiedActionNumber(int value_old, int operator, int operand,
9869                                    int value_min, int value_max)
9870 {
9871   int value_new = (operator == CA_MODE_SET      ? operand :
9872                    operator == CA_MODE_ADD      ? value_old + operand :
9873                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9874                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9875                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9876                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9877                    value_old);
9878
9879   return (value_new < value_min ? value_min :
9880           value_new > value_max ? value_max :
9881           value_new);
9882 }
9883
9884 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9885 {
9886   struct ElementInfo *ei = &element_info[element];
9887   struct ElementChangeInfo *change = &ei->change_page[page];
9888   int target_element = change->target_element;
9889   int action_type = change->action_type;
9890   int action_mode = change->action_mode;
9891   int action_arg = change->action_arg;
9892   int action_element = change->action_element;
9893   int i;
9894
9895   if (!change->has_action)
9896     return;
9897
9898   // ---------- determine action paramater values -----------------------------
9899
9900   int level_time_value =
9901     (level.time > 0 ? TimeLeft :
9902      TimePlayed);
9903
9904   int action_arg_element_raw =
9905     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9906      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9907      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9908      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9909      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9910      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9911      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9912      EL_EMPTY);
9913   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9914
9915   int action_arg_direction =
9916     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9917      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9918      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9919      change->actual_trigger_side :
9920      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9921      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9922      MV_NONE);
9923
9924   int action_arg_number_min =
9925     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9926      CA_ARG_MIN);
9927
9928   int action_arg_number_max =
9929     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9930      action_type == CA_SET_LEVEL_GEMS ? 999 :
9931      action_type == CA_SET_LEVEL_TIME ? 9999 :
9932      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9933      action_type == CA_SET_CE_VALUE ? 9999 :
9934      action_type == CA_SET_CE_SCORE ? 9999 :
9935      CA_ARG_MAX);
9936
9937   int action_arg_number_reset =
9938     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9939      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9940      action_type == CA_SET_LEVEL_TIME ? level.time :
9941      action_type == CA_SET_LEVEL_SCORE ? 0 :
9942      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9943      action_type == CA_SET_CE_SCORE ? 0 :
9944      0);
9945
9946   int action_arg_number =
9947     (action_arg <= CA_ARG_MAX ? action_arg :
9948      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9949      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9950      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9951      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9952      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9953      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9954      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9955      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9956      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9957      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9958      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
9959      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
9960      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9961      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9962      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9963      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9964      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9965      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9966      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9967      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9968      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9969      -1);
9970
9971   int action_arg_number_old =
9972     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
9973      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9974      action_type == CA_SET_LEVEL_SCORE ? game.score :
9975      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9976      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9977      0);
9978
9979   int action_arg_number_new =
9980     getModifiedActionNumber(action_arg_number_old,
9981                             action_mode, action_arg_number,
9982                             action_arg_number_min, action_arg_number_max);
9983
9984   int trigger_player_bits =
9985     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9986      change->actual_trigger_player_bits : change->trigger_player);
9987
9988   int action_arg_player_bits =
9989     (action_arg >= CA_ARG_PLAYER_1 &&
9990      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9991      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9992      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9993      PLAYER_BITS_ANY);
9994
9995   // ---------- execute action  -----------------------------------------------
9996
9997   switch (action_type)
9998   {
9999     case CA_NO_ACTION:
10000     {
10001       return;
10002     }
10003
10004     // ---------- level actions  ----------------------------------------------
10005
10006     case CA_RESTART_LEVEL:
10007     {
10008       game.restart_level = TRUE;
10009
10010       break;
10011     }
10012
10013     case CA_SHOW_ENVELOPE:
10014     {
10015       int element = getSpecialActionElement(action_arg_element,
10016                                             action_arg_number, EL_ENVELOPE_1);
10017
10018       if (IS_ENVELOPE(element))
10019         local_player->show_envelope = element;
10020
10021       break;
10022     }
10023
10024     case CA_SET_LEVEL_TIME:
10025     {
10026       if (level.time > 0)       // only modify limited time value
10027       {
10028         TimeLeft = action_arg_number_new;
10029
10030         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10031
10032         DisplayGameControlValues();
10033
10034         if (!TimeLeft && setup.time_limit)
10035           for (i = 0; i < MAX_PLAYERS; i++)
10036             KillPlayer(&stored_player[i]);
10037       }
10038
10039       break;
10040     }
10041
10042     case CA_SET_LEVEL_SCORE:
10043     {
10044       game.score = action_arg_number_new;
10045
10046       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10047
10048       DisplayGameControlValues();
10049
10050       break;
10051     }
10052
10053     case CA_SET_LEVEL_GEMS:
10054     {
10055       game.gems_still_needed = action_arg_number_new;
10056
10057       game.snapshot.collected_item = TRUE;
10058
10059       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10060
10061       DisplayGameControlValues();
10062
10063       break;
10064     }
10065
10066     case CA_SET_LEVEL_WIND:
10067     {
10068       game.wind_direction = action_arg_direction;
10069
10070       break;
10071     }
10072
10073     case CA_SET_LEVEL_RANDOM_SEED:
10074     {
10075       // ensure that setting a new random seed while playing is predictable
10076       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10077
10078       break;
10079     }
10080
10081     // ---------- player actions  ---------------------------------------------
10082
10083     case CA_MOVE_PLAYER:
10084     case CA_MOVE_PLAYER_NEW:
10085     {
10086       // automatically move to the next field in specified direction
10087       for (i = 0; i < MAX_PLAYERS; i++)
10088         if (trigger_player_bits & (1 << i))
10089           if (action_type == CA_MOVE_PLAYER ||
10090               stored_player[i].MovPos == 0)
10091             stored_player[i].programmed_action = action_arg_direction;
10092
10093       break;
10094     }
10095
10096     case CA_EXIT_PLAYER:
10097     {
10098       for (i = 0; i < MAX_PLAYERS; i++)
10099         if (action_arg_player_bits & (1 << i))
10100           ExitPlayer(&stored_player[i]);
10101
10102       if (game.players_still_needed == 0)
10103         LevelSolved();
10104
10105       break;
10106     }
10107
10108     case CA_KILL_PLAYER:
10109     {
10110       for (i = 0; i < MAX_PLAYERS; i++)
10111         if (action_arg_player_bits & (1 << i))
10112           KillPlayer(&stored_player[i]);
10113
10114       break;
10115     }
10116
10117     case CA_SET_PLAYER_KEYS:
10118     {
10119       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10120       int element = getSpecialActionElement(action_arg_element,
10121                                             action_arg_number, EL_KEY_1);
10122
10123       if (IS_KEY(element))
10124       {
10125         for (i = 0; i < MAX_PLAYERS; i++)
10126         {
10127           if (trigger_player_bits & (1 << i))
10128           {
10129             stored_player[i].key[KEY_NR(element)] = key_state;
10130
10131             DrawGameDoorValues();
10132           }
10133         }
10134       }
10135
10136       break;
10137     }
10138
10139     case CA_SET_PLAYER_SPEED:
10140     {
10141       for (i = 0; i < MAX_PLAYERS; i++)
10142       {
10143         if (trigger_player_bits & (1 << i))
10144         {
10145           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10146
10147           if (action_arg == CA_ARG_SPEED_FASTER &&
10148               stored_player[i].cannot_move)
10149           {
10150             action_arg_number = STEPSIZE_VERY_SLOW;
10151           }
10152           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10153                    action_arg == CA_ARG_SPEED_FASTER)
10154           {
10155             action_arg_number = 2;
10156             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10157                            CA_MODE_MULTIPLY);
10158           }
10159           else if (action_arg == CA_ARG_NUMBER_RESET)
10160           {
10161             action_arg_number = level.initial_player_stepsize[i];
10162           }
10163
10164           move_stepsize =
10165             getModifiedActionNumber(move_stepsize,
10166                                     action_mode,
10167                                     action_arg_number,
10168                                     action_arg_number_min,
10169                                     action_arg_number_max);
10170
10171           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10172         }
10173       }
10174
10175       break;
10176     }
10177
10178     case CA_SET_PLAYER_SHIELD:
10179     {
10180       for (i = 0; i < MAX_PLAYERS; i++)
10181       {
10182         if (trigger_player_bits & (1 << i))
10183         {
10184           if (action_arg == CA_ARG_SHIELD_OFF)
10185           {
10186             stored_player[i].shield_normal_time_left = 0;
10187             stored_player[i].shield_deadly_time_left = 0;
10188           }
10189           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10190           {
10191             stored_player[i].shield_normal_time_left = 999999;
10192           }
10193           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10194           {
10195             stored_player[i].shield_normal_time_left = 999999;
10196             stored_player[i].shield_deadly_time_left = 999999;
10197           }
10198         }
10199       }
10200
10201       break;
10202     }
10203
10204     case CA_SET_PLAYER_GRAVITY:
10205     {
10206       for (i = 0; i < MAX_PLAYERS; i++)
10207       {
10208         if (trigger_player_bits & (1 << i))
10209         {
10210           stored_player[i].gravity =
10211             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10212              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10213              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10214              stored_player[i].gravity);
10215         }
10216       }
10217
10218       break;
10219     }
10220
10221     case CA_SET_PLAYER_ARTWORK:
10222     {
10223       for (i = 0; i < MAX_PLAYERS; i++)
10224       {
10225         if (trigger_player_bits & (1 << i))
10226         {
10227           int artwork_element = action_arg_element;
10228
10229           if (action_arg == CA_ARG_ELEMENT_RESET)
10230             artwork_element =
10231               (level.use_artwork_element[i] ? level.artwork_element[i] :
10232                stored_player[i].element_nr);
10233
10234           if (stored_player[i].artwork_element != artwork_element)
10235             stored_player[i].Frame = 0;
10236
10237           stored_player[i].artwork_element = artwork_element;
10238
10239           SetPlayerWaiting(&stored_player[i], FALSE);
10240
10241           // set number of special actions for bored and sleeping animation
10242           stored_player[i].num_special_action_bored =
10243             get_num_special_action(artwork_element,
10244                                    ACTION_BORING_1, ACTION_BORING_LAST);
10245           stored_player[i].num_special_action_sleeping =
10246             get_num_special_action(artwork_element,
10247                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10248         }
10249       }
10250
10251       break;
10252     }
10253
10254     case CA_SET_PLAYER_INVENTORY:
10255     {
10256       for (i = 0; i < MAX_PLAYERS; i++)
10257       {
10258         struct PlayerInfo *player = &stored_player[i];
10259         int j, k;
10260
10261         if (trigger_player_bits & (1 << i))
10262         {
10263           int inventory_element = action_arg_element;
10264
10265           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10266               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10267               action_arg == CA_ARG_ELEMENT_ACTION)
10268           {
10269             int element = inventory_element;
10270             int collect_count = element_info[element].collect_count_initial;
10271
10272             if (!IS_CUSTOM_ELEMENT(element))
10273               collect_count = 1;
10274
10275             if (collect_count == 0)
10276               player->inventory_infinite_element = element;
10277             else
10278               for (k = 0; k < collect_count; k++)
10279                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10280                   player->inventory_element[player->inventory_size++] =
10281                     element;
10282           }
10283           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10284                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10285                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10286           {
10287             if (player->inventory_infinite_element != EL_UNDEFINED &&
10288                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10289                                      action_arg_element_raw))
10290               player->inventory_infinite_element = EL_UNDEFINED;
10291
10292             for (k = 0, j = 0; j < player->inventory_size; j++)
10293             {
10294               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10295                                         action_arg_element_raw))
10296                 player->inventory_element[k++] = player->inventory_element[j];
10297             }
10298
10299             player->inventory_size = k;
10300           }
10301           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10302           {
10303             if (player->inventory_size > 0)
10304             {
10305               for (j = 0; j < player->inventory_size - 1; j++)
10306                 player->inventory_element[j] = player->inventory_element[j + 1];
10307
10308               player->inventory_size--;
10309             }
10310           }
10311           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10312           {
10313             if (player->inventory_size > 0)
10314               player->inventory_size--;
10315           }
10316           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10317           {
10318             player->inventory_infinite_element = EL_UNDEFINED;
10319             player->inventory_size = 0;
10320           }
10321           else if (action_arg == CA_ARG_INVENTORY_RESET)
10322           {
10323             player->inventory_infinite_element = EL_UNDEFINED;
10324             player->inventory_size = 0;
10325
10326             if (level.use_initial_inventory[i])
10327             {
10328               for (j = 0; j < level.initial_inventory_size[i]; j++)
10329               {
10330                 int element = level.initial_inventory_content[i][j];
10331                 int collect_count = element_info[element].collect_count_initial;
10332
10333                 if (!IS_CUSTOM_ELEMENT(element))
10334                   collect_count = 1;
10335
10336                 if (collect_count == 0)
10337                   player->inventory_infinite_element = element;
10338                 else
10339                   for (k = 0; k < collect_count; k++)
10340                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10341                       player->inventory_element[player->inventory_size++] =
10342                         element;
10343               }
10344             }
10345           }
10346         }
10347       }
10348
10349       break;
10350     }
10351
10352     // ---------- CE actions  -------------------------------------------------
10353
10354     case CA_SET_CE_VALUE:
10355     {
10356       int last_ce_value = CustomValue[x][y];
10357
10358       CustomValue[x][y] = action_arg_number_new;
10359
10360       if (CustomValue[x][y] != last_ce_value)
10361       {
10362         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10363         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10364
10365         if (CustomValue[x][y] == 0)
10366         {
10367           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10368           ChangeCount[x][y] = 0;        // allow at least one more change
10369
10370           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10371           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10372         }
10373       }
10374
10375       break;
10376     }
10377
10378     case CA_SET_CE_SCORE:
10379     {
10380       int last_ce_score = ei->collect_score;
10381
10382       ei->collect_score = action_arg_number_new;
10383
10384       if (ei->collect_score != last_ce_score)
10385       {
10386         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10387         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10388
10389         if (ei->collect_score == 0)
10390         {
10391           int xx, yy;
10392
10393           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10394           ChangeCount[x][y] = 0;        // allow at least one more change
10395
10396           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10397           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10398
10399           /*
10400             This is a very special case that seems to be a mixture between
10401             CheckElementChange() and CheckTriggeredElementChange(): while
10402             the first one only affects single elements that are triggered
10403             directly, the second one affects multiple elements in the playfield
10404             that are triggered indirectly by another element. This is a third
10405             case: Changing the CE score always affects multiple identical CEs,
10406             so every affected CE must be checked, not only the single CE for
10407             which the CE score was changed in the first place (as every instance
10408             of that CE shares the same CE score, and therefore also can change)!
10409           */
10410           SCAN_PLAYFIELD(xx, yy)
10411           {
10412             if (Tile[xx][yy] == element)
10413               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10414                                  CE_SCORE_GETS_ZERO);
10415           }
10416         }
10417       }
10418
10419       break;
10420     }
10421
10422     case CA_SET_CE_ARTWORK:
10423     {
10424       int artwork_element = action_arg_element;
10425       boolean reset_frame = FALSE;
10426       int xx, yy;
10427
10428       if (action_arg == CA_ARG_ELEMENT_RESET)
10429         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10430                            element);
10431
10432       if (ei->gfx_element != artwork_element)
10433         reset_frame = TRUE;
10434
10435       ei->gfx_element = artwork_element;
10436
10437       SCAN_PLAYFIELD(xx, yy)
10438       {
10439         if (Tile[xx][yy] == element)
10440         {
10441           if (reset_frame)
10442           {
10443             ResetGfxAnimation(xx, yy);
10444             ResetRandomAnimationValue(xx, yy);
10445           }
10446
10447           TEST_DrawLevelField(xx, yy);
10448         }
10449       }
10450
10451       break;
10452     }
10453
10454     // ---------- engine actions  ---------------------------------------------
10455
10456     case CA_SET_ENGINE_SCAN_MODE:
10457     {
10458       InitPlayfieldScanMode(action_arg);
10459
10460       break;
10461     }
10462
10463     default:
10464       break;
10465   }
10466 }
10467
10468 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10469 {
10470   int old_element = Tile[x][y];
10471   int new_element = GetElementFromGroupElement(element);
10472   int previous_move_direction = MovDir[x][y];
10473   int last_ce_value = CustomValue[x][y];
10474   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10475   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10476   boolean add_player_onto_element = (new_element_is_player &&
10477                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10478                                      IS_WALKABLE(old_element));
10479
10480   if (!add_player_onto_element)
10481   {
10482     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10483       RemoveMovingField(x, y);
10484     else
10485       RemoveField(x, y);
10486
10487     Tile[x][y] = new_element;
10488
10489     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10490       MovDir[x][y] = previous_move_direction;
10491
10492     if (element_info[new_element].use_last_ce_value)
10493       CustomValue[x][y] = last_ce_value;
10494
10495     InitField_WithBug1(x, y, FALSE);
10496
10497     new_element = Tile[x][y];   // element may have changed
10498
10499     ResetGfxAnimation(x, y);
10500     ResetRandomAnimationValue(x, y);
10501
10502     TEST_DrawLevelField(x, y);
10503
10504     if (GFX_CRUMBLED(new_element))
10505       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10506   }
10507
10508   // check if element under the player changes from accessible to unaccessible
10509   // (needed for special case of dropping element which then changes)
10510   // (must be checked after creating new element for walkable group elements)
10511   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10512       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10513   {
10514     Bang(x, y);
10515
10516     return;
10517   }
10518
10519   // "ChangeCount" not set yet to allow "entered by player" change one time
10520   if (new_element_is_player)
10521     RelocatePlayer(x, y, new_element);
10522
10523   if (is_change)
10524     ChangeCount[x][y]++;        // count number of changes in the same frame
10525
10526   TestIfBadThingTouchesPlayer(x, y);
10527   TestIfPlayerTouchesCustomElement(x, y);
10528   TestIfElementTouchesCustomElement(x, y);
10529 }
10530
10531 static void CreateField(int x, int y, int element)
10532 {
10533   CreateFieldExt(x, y, element, FALSE);
10534 }
10535
10536 static void CreateElementFromChange(int x, int y, int element)
10537 {
10538   element = GET_VALID_RUNTIME_ELEMENT(element);
10539
10540   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10541   {
10542     int old_element = Tile[x][y];
10543
10544     // prevent changed element from moving in same engine frame
10545     // unless both old and new element can either fall or move
10546     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10547         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10548       Stop[x][y] = TRUE;
10549   }
10550
10551   CreateFieldExt(x, y, element, TRUE);
10552 }
10553
10554 static boolean ChangeElement(int x, int y, int element, int page)
10555 {
10556   struct ElementInfo *ei = &element_info[element];
10557   struct ElementChangeInfo *change = &ei->change_page[page];
10558   int ce_value = CustomValue[x][y];
10559   int ce_score = ei->collect_score;
10560   int target_element;
10561   int old_element = Tile[x][y];
10562
10563   // always use default change event to prevent running into a loop
10564   if (ChangeEvent[x][y] == -1)
10565     ChangeEvent[x][y] = CE_DELAY;
10566
10567   if (ChangeEvent[x][y] == CE_DELAY)
10568   {
10569     // reset actual trigger element, trigger player and action element
10570     change->actual_trigger_element = EL_EMPTY;
10571     change->actual_trigger_player = EL_EMPTY;
10572     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10573     change->actual_trigger_side = CH_SIDE_NONE;
10574     change->actual_trigger_ce_value = 0;
10575     change->actual_trigger_ce_score = 0;
10576   }
10577
10578   // do not change elements more than a specified maximum number of changes
10579   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10580     return FALSE;
10581
10582   ChangeCount[x][y]++;          // count number of changes in the same frame
10583
10584   if (change->explode)
10585   {
10586     Bang(x, y);
10587
10588     return TRUE;
10589   }
10590
10591   if (change->use_target_content)
10592   {
10593     boolean complete_replace = TRUE;
10594     boolean can_replace[3][3];
10595     int xx, yy;
10596
10597     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10598     {
10599       boolean is_empty;
10600       boolean is_walkable;
10601       boolean is_diggable;
10602       boolean is_collectible;
10603       boolean is_removable;
10604       boolean is_destructible;
10605       int ex = x + xx - 1;
10606       int ey = y + yy - 1;
10607       int content_element = change->target_content.e[xx][yy];
10608       int e;
10609
10610       can_replace[xx][yy] = TRUE;
10611
10612       if (ex == x && ey == y)   // do not check changing element itself
10613         continue;
10614
10615       if (content_element == EL_EMPTY_SPACE)
10616       {
10617         can_replace[xx][yy] = FALSE;    // do not replace border with space
10618
10619         continue;
10620       }
10621
10622       if (!IN_LEV_FIELD(ex, ey))
10623       {
10624         can_replace[xx][yy] = FALSE;
10625         complete_replace = FALSE;
10626
10627         continue;
10628       }
10629
10630       e = Tile[ex][ey];
10631
10632       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10633         e = MovingOrBlocked2Element(ex, ey);
10634
10635       is_empty = (IS_FREE(ex, ey) ||
10636                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10637
10638       is_walkable     = (is_empty || IS_WALKABLE(e));
10639       is_diggable     = (is_empty || IS_DIGGABLE(e));
10640       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10641       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10642       is_removable    = (is_diggable || is_collectible);
10643
10644       can_replace[xx][yy] =
10645         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10646           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10647           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10648           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10649           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10650           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10651          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10652
10653       if (!can_replace[xx][yy])
10654         complete_replace = FALSE;
10655     }
10656
10657     if (!change->only_if_complete || complete_replace)
10658     {
10659       boolean something_has_changed = FALSE;
10660
10661       if (change->only_if_complete && change->use_random_replace &&
10662           RND(100) < change->random_percentage)
10663         return FALSE;
10664
10665       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10666       {
10667         int ex = x + xx - 1;
10668         int ey = y + yy - 1;
10669         int content_element;
10670
10671         if (can_replace[xx][yy] && (!change->use_random_replace ||
10672                                     RND(100) < change->random_percentage))
10673         {
10674           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10675             RemoveMovingField(ex, ey);
10676
10677           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10678
10679           content_element = change->target_content.e[xx][yy];
10680           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10681                                               ce_value, ce_score);
10682
10683           CreateElementFromChange(ex, ey, target_element);
10684
10685           something_has_changed = TRUE;
10686
10687           // for symmetry reasons, freeze newly created border elements
10688           if (ex != x || ey != y)
10689             Stop[ex][ey] = TRUE;        // no more moving in this frame
10690         }
10691       }
10692
10693       if (something_has_changed)
10694       {
10695         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10696         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10697       }
10698     }
10699   }
10700   else
10701   {
10702     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10703                                         ce_value, ce_score);
10704
10705     if (element == EL_DIAGONAL_GROWING ||
10706         element == EL_DIAGONAL_SHRINKING)
10707     {
10708       target_element = Store[x][y];
10709
10710       Store[x][y] = EL_EMPTY;
10711     }
10712
10713     CreateElementFromChange(x, y, target_element);
10714
10715     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10716     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10717   }
10718
10719   // this uses direct change before indirect change
10720   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10721
10722   return TRUE;
10723 }
10724
10725 static void HandleElementChange(int x, int y, int page)
10726 {
10727   int element = MovingOrBlocked2Element(x, y);
10728   struct ElementInfo *ei = &element_info[element];
10729   struct ElementChangeInfo *change = &ei->change_page[page];
10730   boolean handle_action_before_change = FALSE;
10731
10732 #ifdef DEBUG
10733   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10734       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10735   {
10736     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10737           x, y, element, element_info[element].token_name);
10738     Debug("game:playing:HandleElementChange", "This should never happen!");
10739   }
10740 #endif
10741
10742   // this can happen with classic bombs on walkable, changing elements
10743   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10744   {
10745     return;
10746   }
10747
10748   if (ChangeDelay[x][y] == 0)           // initialize element change
10749   {
10750     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10751
10752     if (change->can_change)
10753     {
10754       // !!! not clear why graphic animation should be reset at all here !!!
10755       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10756       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10757
10758       /*
10759         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10760
10761         When using an animation frame delay of 1 (this only happens with
10762         "sp_zonk.moving.left/right" in the classic graphics), the default
10763         (non-moving) animation shows wrong animation frames (while the
10764         moving animation, like "sp_zonk.moving.left/right", is correct,
10765         so this graphical bug never shows up with the classic graphics).
10766         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10767         be drawn instead of the correct frames 0,1,2,3. This is caused by
10768         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10769         an element change: First when the change delay ("ChangeDelay[][]")
10770         counter has reached zero after decrementing, then a second time in
10771         the next frame (after "GfxFrame[][]" was already incremented) when
10772         "ChangeDelay[][]" is reset to the initial delay value again.
10773
10774         This causes frame 0 to be drawn twice, while the last frame won't
10775         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10776
10777         As some animations may already be cleverly designed around this bug
10778         (at least the "Snake Bite" snake tail animation does this), it cannot
10779         simply be fixed here without breaking such existing animations.
10780         Unfortunately, it cannot easily be detected if a graphics set was
10781         designed "before" or "after" the bug was fixed. As a workaround,
10782         a new graphics set option "game.graphics_engine_version" was added
10783         to be able to specify the game's major release version for which the
10784         graphics set was designed, which can then be used to decide if the
10785         bugfix should be used (version 4 and above) or not (version 3 or
10786         below, or if no version was specified at all, as with old sets).
10787
10788         (The wrong/fixed animation frames can be tested with the test level set
10789         "test_gfxframe" and level "000", which contains a specially prepared
10790         custom element at level position (x/y) == (11/9) which uses the zonk
10791         animation mentioned above. Using "game.graphics_engine_version: 4"
10792         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10793         This can also be seen from the debug output for this test element.)
10794       */
10795
10796       // when a custom element is about to change (for example by change delay),
10797       // do not reset graphic animation when the custom element is moving
10798       if (game.graphics_engine_version < 4 &&
10799           !IS_MOVING(x, y))
10800       {
10801         ResetGfxAnimation(x, y);
10802         ResetRandomAnimationValue(x, y);
10803       }
10804
10805       if (change->pre_change_function)
10806         change->pre_change_function(x, y);
10807     }
10808   }
10809
10810   ChangeDelay[x][y]--;
10811
10812   if (ChangeDelay[x][y] != 0)           // continue element change
10813   {
10814     if (change->can_change)
10815     {
10816       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10817
10818       if (IS_ANIMATED(graphic))
10819         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10820
10821       if (change->change_function)
10822         change->change_function(x, y);
10823     }
10824   }
10825   else                                  // finish element change
10826   {
10827     if (ChangePage[x][y] != -1)         // remember page from delayed change
10828     {
10829       page = ChangePage[x][y];
10830       ChangePage[x][y] = -1;
10831
10832       change = &ei->change_page[page];
10833     }
10834
10835     if (IS_MOVING(x, y))                // never change a running system ;-)
10836     {
10837       ChangeDelay[x][y] = 1;            // try change after next move step
10838       ChangePage[x][y] = page;          // remember page to use for change
10839
10840       return;
10841     }
10842
10843     // special case: set new level random seed before changing element
10844     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10845       handle_action_before_change = TRUE;
10846
10847     if (change->has_action && handle_action_before_change)
10848       ExecuteCustomElementAction(x, y, element, page);
10849
10850     if (change->can_change)
10851     {
10852       if (ChangeElement(x, y, element, page))
10853       {
10854         if (change->post_change_function)
10855           change->post_change_function(x, y);
10856       }
10857     }
10858
10859     if (change->has_action && !handle_action_before_change)
10860       ExecuteCustomElementAction(x, y, element, page);
10861   }
10862 }
10863
10864 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10865                                               int trigger_element,
10866                                               int trigger_event,
10867                                               int trigger_player,
10868                                               int trigger_side,
10869                                               int trigger_page)
10870 {
10871   boolean change_done_any = FALSE;
10872   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10873   int i;
10874
10875   if (!(trigger_events[trigger_element][trigger_event]))
10876     return FALSE;
10877
10878   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10879
10880   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10881   {
10882     int element = EL_CUSTOM_START + i;
10883     boolean change_done = FALSE;
10884     int p;
10885
10886     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10887         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10888       continue;
10889
10890     for (p = 0; p < element_info[element].num_change_pages; p++)
10891     {
10892       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10893
10894       if (change->can_change_or_has_action &&
10895           change->has_event[trigger_event] &&
10896           change->trigger_side & trigger_side &&
10897           change->trigger_player & trigger_player &&
10898           change->trigger_page & trigger_page_bits &&
10899           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10900       {
10901         change->actual_trigger_element = trigger_element;
10902         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10903         change->actual_trigger_player_bits = trigger_player;
10904         change->actual_trigger_side = trigger_side;
10905         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10906         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10907
10908         if ((change->can_change && !change_done) || change->has_action)
10909         {
10910           int x, y;
10911
10912           SCAN_PLAYFIELD(x, y)
10913           {
10914             if (Tile[x][y] == element)
10915             {
10916               if (change->can_change && !change_done)
10917               {
10918                 // if element already changed in this frame, not only prevent
10919                 // another element change (checked in ChangeElement()), but
10920                 // also prevent additional element actions for this element
10921
10922                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10923                     !level.use_action_after_change_bug)
10924                   continue;
10925
10926                 ChangeDelay[x][y] = 1;
10927                 ChangeEvent[x][y] = trigger_event;
10928
10929                 HandleElementChange(x, y, p);
10930               }
10931               else if (change->has_action)
10932               {
10933                 // if element already changed in this frame, not only prevent
10934                 // another element change (checked in ChangeElement()), but
10935                 // also prevent additional element actions for this element
10936
10937                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10938                     !level.use_action_after_change_bug)
10939                   continue;
10940
10941                 ExecuteCustomElementAction(x, y, element, p);
10942                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10943               }
10944             }
10945           }
10946
10947           if (change->can_change)
10948           {
10949             change_done = TRUE;
10950             change_done_any = TRUE;
10951           }
10952         }
10953       }
10954     }
10955   }
10956
10957   RECURSION_LOOP_DETECTION_END();
10958
10959   return change_done_any;
10960 }
10961
10962 static boolean CheckElementChangeExt(int x, int y,
10963                                      int element,
10964                                      int trigger_element,
10965                                      int trigger_event,
10966                                      int trigger_player,
10967                                      int trigger_side)
10968 {
10969   boolean change_done = FALSE;
10970   int p;
10971
10972   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10973       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10974     return FALSE;
10975
10976   if (Tile[x][y] == EL_BLOCKED)
10977   {
10978     Blocked2Moving(x, y, &x, &y);
10979     element = Tile[x][y];
10980   }
10981
10982   // check if element has already changed or is about to change after moving
10983   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10984        Tile[x][y] != element) ||
10985
10986       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10987        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10988         ChangePage[x][y] != -1)))
10989     return FALSE;
10990
10991   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10992
10993   for (p = 0; p < element_info[element].num_change_pages; p++)
10994   {
10995     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10996
10997     /* check trigger element for all events where the element that is checked
10998        for changing interacts with a directly adjacent element -- this is
10999        different to element changes that affect other elements to change on the
11000        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11001     boolean check_trigger_element =
11002       (trigger_event == CE_TOUCHING_X ||
11003        trigger_event == CE_HITTING_X ||
11004        trigger_event == CE_HIT_BY_X ||
11005        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11006
11007     if (change->can_change_or_has_action &&
11008         change->has_event[trigger_event] &&
11009         change->trigger_side & trigger_side &&
11010         change->trigger_player & trigger_player &&
11011         (!check_trigger_element ||
11012          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11013     {
11014       change->actual_trigger_element = trigger_element;
11015       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11016       change->actual_trigger_player_bits = trigger_player;
11017       change->actual_trigger_side = trigger_side;
11018       change->actual_trigger_ce_value = CustomValue[x][y];
11019       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11020
11021       // special case: trigger element not at (x,y) position for some events
11022       if (check_trigger_element)
11023       {
11024         static struct
11025         {
11026           int dx, dy;
11027         } move_xy[] =
11028           {
11029             {  0,  0 },
11030             { -1,  0 },
11031             { +1,  0 },
11032             {  0,  0 },
11033             {  0, -1 },
11034             {  0,  0 }, { 0, 0 }, { 0, 0 },
11035             {  0, +1 }
11036           };
11037
11038         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11039         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11040
11041         change->actual_trigger_ce_value = CustomValue[xx][yy];
11042         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11043       }
11044
11045       if (change->can_change && !change_done)
11046       {
11047         ChangeDelay[x][y] = 1;
11048         ChangeEvent[x][y] = trigger_event;
11049
11050         HandleElementChange(x, y, p);
11051
11052         change_done = TRUE;
11053       }
11054       else if (change->has_action)
11055       {
11056         ExecuteCustomElementAction(x, y, element, p);
11057         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11058       }
11059     }
11060   }
11061
11062   RECURSION_LOOP_DETECTION_END();
11063
11064   return change_done;
11065 }
11066
11067 static void PlayPlayerSound(struct PlayerInfo *player)
11068 {
11069   int jx = player->jx, jy = player->jy;
11070   int sound_element = player->artwork_element;
11071   int last_action = player->last_action_waiting;
11072   int action = player->action_waiting;
11073
11074   if (player->is_waiting)
11075   {
11076     if (action != last_action)
11077       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11078     else
11079       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11080   }
11081   else
11082   {
11083     if (action != last_action)
11084       StopSound(element_info[sound_element].sound[last_action]);
11085
11086     if (last_action == ACTION_SLEEPING)
11087       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11088   }
11089 }
11090
11091 static void PlayAllPlayersSound(void)
11092 {
11093   int i;
11094
11095   for (i = 0; i < MAX_PLAYERS; i++)
11096     if (stored_player[i].active)
11097       PlayPlayerSound(&stored_player[i]);
11098 }
11099
11100 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11101 {
11102   boolean last_waiting = player->is_waiting;
11103   int move_dir = player->MovDir;
11104
11105   player->dir_waiting = move_dir;
11106   player->last_action_waiting = player->action_waiting;
11107
11108   if (is_waiting)
11109   {
11110     if (!last_waiting)          // not waiting -> waiting
11111     {
11112       player->is_waiting = TRUE;
11113
11114       player->frame_counter_bored =
11115         FrameCounter +
11116         game.player_boring_delay_fixed +
11117         GetSimpleRandom(game.player_boring_delay_random);
11118       player->frame_counter_sleeping =
11119         FrameCounter +
11120         game.player_sleeping_delay_fixed +
11121         GetSimpleRandom(game.player_sleeping_delay_random);
11122
11123       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11124     }
11125
11126     if (game.player_sleeping_delay_fixed +
11127         game.player_sleeping_delay_random > 0 &&
11128         player->anim_delay_counter == 0 &&
11129         player->post_delay_counter == 0 &&
11130         FrameCounter >= player->frame_counter_sleeping)
11131       player->is_sleeping = TRUE;
11132     else if (game.player_boring_delay_fixed +
11133              game.player_boring_delay_random > 0 &&
11134              FrameCounter >= player->frame_counter_bored)
11135       player->is_bored = TRUE;
11136
11137     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11138                               player->is_bored ? ACTION_BORING :
11139                               ACTION_WAITING);
11140
11141     if (player->is_sleeping && player->use_murphy)
11142     {
11143       // special case for sleeping Murphy when leaning against non-free tile
11144
11145       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11146           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11147            !IS_MOVING(player->jx - 1, player->jy)))
11148         move_dir = MV_LEFT;
11149       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11150                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11151                 !IS_MOVING(player->jx + 1, player->jy)))
11152         move_dir = MV_RIGHT;
11153       else
11154         player->is_sleeping = FALSE;
11155
11156       player->dir_waiting = move_dir;
11157     }
11158
11159     if (player->is_sleeping)
11160     {
11161       if (player->num_special_action_sleeping > 0)
11162       {
11163         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11164         {
11165           int last_special_action = player->special_action_sleeping;
11166           int num_special_action = player->num_special_action_sleeping;
11167           int special_action =
11168             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11169              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11170              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11171              last_special_action + 1 : ACTION_SLEEPING);
11172           int special_graphic =
11173             el_act_dir2img(player->artwork_element, special_action, move_dir);
11174
11175           player->anim_delay_counter =
11176             graphic_info[special_graphic].anim_delay_fixed +
11177             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11178           player->post_delay_counter =
11179             graphic_info[special_graphic].post_delay_fixed +
11180             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11181
11182           player->special_action_sleeping = special_action;
11183         }
11184
11185         if (player->anim_delay_counter > 0)
11186         {
11187           player->action_waiting = player->special_action_sleeping;
11188           player->anim_delay_counter--;
11189         }
11190         else if (player->post_delay_counter > 0)
11191         {
11192           player->post_delay_counter--;
11193         }
11194       }
11195     }
11196     else if (player->is_bored)
11197     {
11198       if (player->num_special_action_bored > 0)
11199       {
11200         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11201         {
11202           int special_action =
11203             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11204           int special_graphic =
11205             el_act_dir2img(player->artwork_element, special_action, move_dir);
11206
11207           player->anim_delay_counter =
11208             graphic_info[special_graphic].anim_delay_fixed +
11209             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11210           player->post_delay_counter =
11211             graphic_info[special_graphic].post_delay_fixed +
11212             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11213
11214           player->special_action_bored = special_action;
11215         }
11216
11217         if (player->anim_delay_counter > 0)
11218         {
11219           player->action_waiting = player->special_action_bored;
11220           player->anim_delay_counter--;
11221         }
11222         else if (player->post_delay_counter > 0)
11223         {
11224           player->post_delay_counter--;
11225         }
11226       }
11227     }
11228   }
11229   else if (last_waiting)        // waiting -> not waiting
11230   {
11231     player->is_waiting = FALSE;
11232     player->is_bored = FALSE;
11233     player->is_sleeping = FALSE;
11234
11235     player->frame_counter_bored = -1;
11236     player->frame_counter_sleeping = -1;
11237
11238     player->anim_delay_counter = 0;
11239     player->post_delay_counter = 0;
11240
11241     player->dir_waiting = player->MovDir;
11242     player->action_waiting = ACTION_DEFAULT;
11243
11244     player->special_action_bored = ACTION_DEFAULT;
11245     player->special_action_sleeping = ACTION_DEFAULT;
11246   }
11247 }
11248
11249 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11250 {
11251   if ((!player->is_moving  && player->was_moving) ||
11252       (player->MovPos == 0 && player->was_moving) ||
11253       (player->is_snapping && !player->was_snapping) ||
11254       (player->is_dropping && !player->was_dropping))
11255   {
11256     if (!CheckSaveEngineSnapshotToList())
11257       return;
11258
11259     player->was_moving = FALSE;
11260     player->was_snapping = TRUE;
11261     player->was_dropping = TRUE;
11262   }
11263   else
11264   {
11265     if (player->is_moving)
11266       player->was_moving = TRUE;
11267
11268     if (!player->is_snapping)
11269       player->was_snapping = FALSE;
11270
11271     if (!player->is_dropping)
11272       player->was_dropping = FALSE;
11273   }
11274
11275   static struct MouseActionInfo mouse_action_last = { 0 };
11276   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11277   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11278
11279   if (new_released)
11280     CheckSaveEngineSnapshotToList();
11281
11282   mouse_action_last = mouse_action;
11283 }
11284
11285 static void CheckSingleStepMode(struct PlayerInfo *player)
11286 {
11287   if (tape.single_step && tape.recording && !tape.pausing)
11288   {
11289     // as it is called "single step mode", just return to pause mode when the
11290     // player stopped moving after one tile (or never starts moving at all)
11291     // (reverse logic needed here in case single step mode used in team mode)
11292     if (player->is_moving ||
11293         player->is_pushing ||
11294         player->is_dropping_pressed ||
11295         player->effective_mouse_action.button)
11296       game.enter_single_step_mode = FALSE;
11297   }
11298
11299   CheckSaveEngineSnapshot(player);
11300 }
11301
11302 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11303 {
11304   int left      = player_action & JOY_LEFT;
11305   int right     = player_action & JOY_RIGHT;
11306   int up        = player_action & JOY_UP;
11307   int down      = player_action & JOY_DOWN;
11308   int button1   = player_action & JOY_BUTTON_1;
11309   int button2   = player_action & JOY_BUTTON_2;
11310   int dx        = (left ? -1 : right ? 1 : 0);
11311   int dy        = (up   ? -1 : down  ? 1 : 0);
11312
11313   if (!player->active || tape.pausing)
11314     return 0;
11315
11316   if (player_action)
11317   {
11318     if (button1)
11319       SnapField(player, dx, dy);
11320     else
11321     {
11322       if (button2)
11323         DropElement(player);
11324
11325       MovePlayer(player, dx, dy);
11326     }
11327
11328     CheckSingleStepMode(player);
11329
11330     SetPlayerWaiting(player, FALSE);
11331
11332     return player_action;
11333   }
11334   else
11335   {
11336     // no actions for this player (no input at player's configured device)
11337
11338     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11339     SnapField(player, 0, 0);
11340     CheckGravityMovementWhenNotMoving(player);
11341
11342     if (player->MovPos == 0)
11343       SetPlayerWaiting(player, TRUE);
11344
11345     if (player->MovPos == 0)    // needed for tape.playing
11346       player->is_moving = FALSE;
11347
11348     player->is_dropping = FALSE;
11349     player->is_dropping_pressed = FALSE;
11350     player->drop_pressed_delay = 0;
11351
11352     CheckSingleStepMode(player);
11353
11354     return 0;
11355   }
11356 }
11357
11358 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11359                                          byte *tape_action)
11360 {
11361   if (!tape.use_mouse_actions)
11362     return;
11363
11364   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11365   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11366   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11367 }
11368
11369 static void SetTapeActionFromMouseAction(byte *tape_action,
11370                                          struct MouseActionInfo *mouse_action)
11371 {
11372   if (!tape.use_mouse_actions)
11373     return;
11374
11375   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11376   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11377   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11378 }
11379
11380 static void CheckLevelSolved(void)
11381 {
11382   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11383   {
11384     if (game_em.level_solved &&
11385         !game_em.game_over)                             // game won
11386     {
11387       LevelSolved();
11388
11389       game_em.game_over = TRUE;
11390
11391       game.all_players_gone = TRUE;
11392     }
11393
11394     if (game_em.game_over)                              // game lost
11395       game.all_players_gone = TRUE;
11396   }
11397   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11398   {
11399     if (game_sp.level_solved &&
11400         !game_sp.game_over)                             // game won
11401     {
11402       LevelSolved();
11403
11404       game_sp.game_over = TRUE;
11405
11406       game.all_players_gone = TRUE;
11407     }
11408
11409     if (game_sp.game_over)                              // game lost
11410       game.all_players_gone = TRUE;
11411   }
11412   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11413   {
11414     if (game_mm.level_solved &&
11415         !game_mm.game_over)                             // game won
11416     {
11417       LevelSolved();
11418
11419       game_mm.game_over = TRUE;
11420
11421       game.all_players_gone = TRUE;
11422     }
11423
11424     if (game_mm.game_over)                              // game lost
11425       game.all_players_gone = TRUE;
11426   }
11427 }
11428
11429 static void CheckLevelTime(void)
11430 {
11431   int i;
11432
11433   if (TimeFrames >= FRAMES_PER_SECOND)
11434   {
11435     TimeFrames = 0;
11436     TapeTime++;
11437
11438     for (i = 0; i < MAX_PLAYERS; i++)
11439     {
11440       struct PlayerInfo *player = &stored_player[i];
11441
11442       if (SHIELD_ON(player))
11443       {
11444         player->shield_normal_time_left--;
11445
11446         if (player->shield_deadly_time_left > 0)
11447           player->shield_deadly_time_left--;
11448       }
11449     }
11450
11451     if (!game.LevelSolved && !level.use_step_counter)
11452     {
11453       TimePlayed++;
11454
11455       if (TimeLeft > 0)
11456       {
11457         TimeLeft--;
11458
11459         if (TimeLeft <= 10 && setup.time_limit)
11460           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11461
11462         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11463            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11464
11465         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11466
11467         if (!TimeLeft && setup.time_limit)
11468         {
11469           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11470             game_em.lev->killed_out_of_time = TRUE;
11471           else
11472             for (i = 0; i < MAX_PLAYERS; i++)
11473               KillPlayer(&stored_player[i]);
11474         }
11475       }
11476       else if (game.no_time_limit && !game.all_players_gone)
11477       {
11478         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11479       }
11480
11481       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11482     }
11483
11484     if (tape.recording || tape.playing)
11485       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11486   }
11487
11488   if (tape.recording || tape.playing)
11489     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11490
11491   UpdateAndDisplayGameControlValues();
11492 }
11493
11494 void AdvanceFrameAndPlayerCounters(int player_nr)
11495 {
11496   int i;
11497
11498   // advance frame counters (global frame counter and time frame counter)
11499   FrameCounter++;
11500   TimeFrames++;
11501
11502   // advance player counters (counters for move delay, move animation etc.)
11503   for (i = 0; i < MAX_PLAYERS; i++)
11504   {
11505     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11506     int move_delay_value = stored_player[i].move_delay_value;
11507     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11508
11509     if (!advance_player_counters)       // not all players may be affected
11510       continue;
11511
11512     if (move_frames == 0)       // less than one move per game frame
11513     {
11514       int stepsize = TILEX / move_delay_value;
11515       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11516       int count = (stored_player[i].is_moving ?
11517                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11518
11519       if (count % delay == 0)
11520         move_frames = 1;
11521     }
11522
11523     stored_player[i].Frame += move_frames;
11524
11525     if (stored_player[i].MovPos != 0)
11526       stored_player[i].StepFrame += move_frames;
11527
11528     if (stored_player[i].move_delay > 0)
11529       stored_player[i].move_delay--;
11530
11531     // due to bugs in previous versions, counter must count up, not down
11532     if (stored_player[i].push_delay != -1)
11533       stored_player[i].push_delay++;
11534
11535     if (stored_player[i].drop_delay > 0)
11536       stored_player[i].drop_delay--;
11537
11538     if (stored_player[i].is_dropping_pressed)
11539       stored_player[i].drop_pressed_delay++;
11540   }
11541 }
11542
11543 void StartGameActions(boolean init_network_game, boolean record_tape,
11544                       int random_seed)
11545 {
11546   unsigned int new_random_seed = InitRND(random_seed);
11547
11548   if (record_tape)
11549     TapeStartRecording(new_random_seed);
11550
11551   if (init_network_game)
11552   {
11553     SendToServer_LevelFile();
11554     SendToServer_StartPlaying();
11555
11556     return;
11557   }
11558
11559   InitGame();
11560 }
11561
11562 static void GameActionsExt(void)
11563 {
11564 #if 0
11565   static unsigned int game_frame_delay = 0;
11566 #endif
11567   unsigned int game_frame_delay_value;
11568   byte *recorded_player_action;
11569   byte summarized_player_action = 0;
11570   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11571   int i;
11572
11573   // detect endless loops, caused by custom element programming
11574   if (recursion_loop_detected && recursion_loop_depth == 0)
11575   {
11576     char *message = getStringCat3("Internal Error! Element ",
11577                                   EL_NAME(recursion_loop_element),
11578                                   " caused endless loop! Quit the game?");
11579
11580     Warn("element '%s' caused endless loop in game engine",
11581          EL_NAME(recursion_loop_element));
11582
11583     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11584
11585     recursion_loop_detected = FALSE;    // if game should be continued
11586
11587     free(message);
11588
11589     return;
11590   }
11591
11592   if (game.restart_level)
11593     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11594
11595   CheckLevelSolved();
11596
11597   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11598     GameWon();
11599
11600   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11601     TapeStop();
11602
11603   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11604     return;
11605
11606   game_frame_delay_value =
11607     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11608
11609   if (tape.playing && tape.warp_forward && !tape.pausing)
11610     game_frame_delay_value = 0;
11611
11612   SetVideoFrameDelay(game_frame_delay_value);
11613
11614   // (de)activate virtual buttons depending on current game status
11615   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11616   {
11617     if (game.all_players_gone)  // if no players there to be controlled anymore
11618       SetOverlayActive(FALSE);
11619     else if (!tape.playing)     // if game continues after tape stopped playing
11620       SetOverlayActive(TRUE);
11621   }
11622
11623 #if 0
11624 #if 0
11625   // ---------- main game synchronization point ----------
11626
11627   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11628
11629   Debug("game:playing:skip", "skip == %d", skip);
11630
11631 #else
11632   // ---------- main game synchronization point ----------
11633
11634   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11635 #endif
11636 #endif
11637
11638   if (network_playing && !network_player_action_received)
11639   {
11640     // try to get network player actions in time
11641
11642     // last chance to get network player actions without main loop delay
11643     HandleNetworking();
11644
11645     // game was quit by network peer
11646     if (game_status != GAME_MODE_PLAYING)
11647       return;
11648
11649     // check if network player actions still missing and game still running
11650     if (!network_player_action_received && !checkGameEnded())
11651       return;           // failed to get network player actions in time
11652
11653     // do not yet reset "network_player_action_received" (for tape.pausing)
11654   }
11655
11656   if (tape.pausing)
11657     return;
11658
11659   // at this point we know that we really continue executing the game
11660
11661   network_player_action_received = FALSE;
11662
11663   // when playing tape, read previously recorded player input from tape data
11664   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11665
11666   local_player->effective_mouse_action = local_player->mouse_action;
11667
11668   if (recorded_player_action != NULL)
11669     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11670                                  recorded_player_action);
11671
11672   // TapePlayAction() may return NULL when toggling to "pause before death"
11673   if (tape.pausing)
11674     return;
11675
11676   if (tape.set_centered_player)
11677   {
11678     game.centered_player_nr_next = tape.centered_player_nr_next;
11679     game.set_centered_player = TRUE;
11680   }
11681
11682   for (i = 0; i < MAX_PLAYERS; i++)
11683   {
11684     summarized_player_action |= stored_player[i].action;
11685
11686     if (!network_playing && (game.team_mode || tape.playing))
11687       stored_player[i].effective_action = stored_player[i].action;
11688   }
11689
11690   if (network_playing && !checkGameEnded())
11691     SendToServer_MovePlayer(summarized_player_action);
11692
11693   // summarize all actions at local players mapped input device position
11694   // (this allows using different input devices in single player mode)
11695   if (!network.enabled && !game.team_mode)
11696     stored_player[map_player_action[local_player->index_nr]].effective_action =
11697       summarized_player_action;
11698
11699   // summarize all actions at centered player in local team mode
11700   if (tape.recording &&
11701       setup.team_mode && !network.enabled &&
11702       setup.input_on_focus &&
11703       game.centered_player_nr != -1)
11704   {
11705     for (i = 0; i < MAX_PLAYERS; i++)
11706       stored_player[map_player_action[i]].effective_action =
11707         (i == game.centered_player_nr ? summarized_player_action : 0);
11708   }
11709
11710   if (recorded_player_action != NULL)
11711     for (i = 0; i < MAX_PLAYERS; i++)
11712       stored_player[i].effective_action = recorded_player_action[i];
11713
11714   for (i = 0; i < MAX_PLAYERS; i++)
11715   {
11716     tape_action[i] = stored_player[i].effective_action;
11717
11718     /* (this may happen in the RND game engine if a player was not present on
11719        the playfield on level start, but appeared later from a custom element */
11720     if (setup.team_mode &&
11721         tape.recording &&
11722         tape_action[i] &&
11723         !tape.player_participates[i])
11724       tape.player_participates[i] = TRUE;
11725   }
11726
11727   SetTapeActionFromMouseAction(tape_action,
11728                                &local_player->effective_mouse_action);
11729
11730   // only record actions from input devices, but not programmed actions
11731   if (tape.recording)
11732     TapeRecordAction(tape_action);
11733
11734   // remember if game was played (especially after tape stopped playing)
11735   if (!tape.playing && summarized_player_action)
11736     game.GamePlayed = TRUE;
11737
11738 #if USE_NEW_PLAYER_ASSIGNMENTS
11739   // !!! also map player actions in single player mode !!!
11740   // if (game.team_mode)
11741   if (1)
11742   {
11743     byte mapped_action[MAX_PLAYERS];
11744
11745 #if DEBUG_PLAYER_ACTIONS
11746     for (i = 0; i < MAX_PLAYERS; i++)
11747       DebugContinued("", "%d, ", stored_player[i].effective_action);
11748 #endif
11749
11750     for (i = 0; i < MAX_PLAYERS; i++)
11751       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11752
11753     for (i = 0; i < MAX_PLAYERS; i++)
11754       stored_player[i].effective_action = mapped_action[i];
11755
11756 #if DEBUG_PLAYER_ACTIONS
11757     DebugContinued("", "=> ");
11758     for (i = 0; i < MAX_PLAYERS; i++)
11759       DebugContinued("", "%d, ", stored_player[i].effective_action);
11760     DebugContinued("game:playing:player", "\n");
11761 #endif
11762   }
11763 #if DEBUG_PLAYER_ACTIONS
11764   else
11765   {
11766     for (i = 0; i < MAX_PLAYERS; i++)
11767       DebugContinued("", "%d, ", stored_player[i].effective_action);
11768     DebugContinued("game:playing:player", "\n");
11769   }
11770 #endif
11771 #endif
11772
11773   for (i = 0; i < MAX_PLAYERS; i++)
11774   {
11775     // allow engine snapshot in case of changed movement attempt
11776     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11777         (stored_player[i].effective_action & KEY_MOTION))
11778       game.snapshot.changed_action = TRUE;
11779
11780     // allow engine snapshot in case of snapping/dropping attempt
11781     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11782         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11783       game.snapshot.changed_action = TRUE;
11784
11785     game.snapshot.last_action[i] = stored_player[i].effective_action;
11786   }
11787
11788   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11789   {
11790     GameActions_EM_Main();
11791   }
11792   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11793   {
11794     GameActions_SP_Main();
11795   }
11796   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11797   {
11798     GameActions_MM_Main();
11799   }
11800   else
11801   {
11802     GameActions_RND_Main();
11803   }
11804
11805   BlitScreenToBitmap(backbuffer);
11806
11807   CheckLevelSolved();
11808   CheckLevelTime();
11809
11810   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11811
11812   if (global.show_frames_per_second)
11813   {
11814     static unsigned int fps_counter = 0;
11815     static int fps_frames = 0;
11816     unsigned int fps_delay_ms = Counter() - fps_counter;
11817
11818     fps_frames++;
11819
11820     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11821     {
11822       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11823
11824       fps_frames = 0;
11825       fps_counter = Counter();
11826
11827       // always draw FPS to screen after FPS value was updated
11828       redraw_mask |= REDRAW_FPS;
11829     }
11830
11831     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11832     if (GetDrawDeactivationMask() == REDRAW_NONE)
11833       redraw_mask |= REDRAW_FPS;
11834   }
11835 }
11836
11837 static void GameActions_CheckSaveEngineSnapshot(void)
11838 {
11839   if (!game.snapshot.save_snapshot)
11840     return;
11841
11842   // clear flag for saving snapshot _before_ saving snapshot
11843   game.snapshot.save_snapshot = FALSE;
11844
11845   SaveEngineSnapshotToList();
11846 }
11847
11848 void GameActions(void)
11849 {
11850   GameActionsExt();
11851
11852   GameActions_CheckSaveEngineSnapshot();
11853 }
11854
11855 void GameActions_EM_Main(void)
11856 {
11857   byte effective_action[MAX_PLAYERS];
11858   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11859   int i;
11860
11861   for (i = 0; i < MAX_PLAYERS; i++)
11862     effective_action[i] = stored_player[i].effective_action;
11863
11864   GameActions_EM(effective_action, warp_mode);
11865 }
11866
11867 void GameActions_SP_Main(void)
11868 {
11869   byte effective_action[MAX_PLAYERS];
11870   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11871   int i;
11872
11873   for (i = 0; i < MAX_PLAYERS; i++)
11874     effective_action[i] = stored_player[i].effective_action;
11875
11876   GameActions_SP(effective_action, warp_mode);
11877
11878   for (i = 0; i < MAX_PLAYERS; i++)
11879   {
11880     if (stored_player[i].force_dropping)
11881       stored_player[i].action |= KEY_BUTTON_DROP;
11882
11883     stored_player[i].force_dropping = FALSE;
11884   }
11885 }
11886
11887 void GameActions_MM_Main(void)
11888 {
11889   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11890
11891   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11892 }
11893
11894 void GameActions_RND_Main(void)
11895 {
11896   GameActions_RND();
11897 }
11898
11899 void GameActions_RND(void)
11900 {
11901   static struct MouseActionInfo mouse_action_last = { 0 };
11902   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
11903   int magic_wall_x = 0, magic_wall_y = 0;
11904   int i, x, y, element, graphic, last_gfx_frame;
11905
11906   InitPlayfieldScanModeVars();
11907
11908   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11909   {
11910     SCAN_PLAYFIELD(x, y)
11911     {
11912       ChangeCount[x][y] = 0;
11913       ChangeEvent[x][y] = -1;
11914     }
11915   }
11916
11917   if (game.set_centered_player)
11918   {
11919     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11920
11921     // switching to "all players" only possible if all players fit to screen
11922     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11923     {
11924       game.centered_player_nr_next = game.centered_player_nr;
11925       game.set_centered_player = FALSE;
11926     }
11927
11928     // do not switch focus to non-existing (or non-active) player
11929     if (game.centered_player_nr_next >= 0 &&
11930         !stored_player[game.centered_player_nr_next].active)
11931     {
11932       game.centered_player_nr_next = game.centered_player_nr;
11933       game.set_centered_player = FALSE;
11934     }
11935   }
11936
11937   if (game.set_centered_player &&
11938       ScreenMovPos == 0)        // screen currently aligned at tile position
11939   {
11940     int sx, sy;
11941
11942     if (game.centered_player_nr_next == -1)
11943     {
11944       setScreenCenteredToAllPlayers(&sx, &sy);
11945     }
11946     else
11947     {
11948       sx = stored_player[game.centered_player_nr_next].jx;
11949       sy = stored_player[game.centered_player_nr_next].jy;
11950     }
11951
11952     game.centered_player_nr = game.centered_player_nr_next;
11953     game.set_centered_player = FALSE;
11954
11955     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11956     DrawGameDoorValues();
11957   }
11958
11959   // check single step mode (set flag and clear again if any player is active)
11960   game.enter_single_step_mode =
11961     (tape.single_step && tape.recording && !tape.pausing);
11962
11963   for (i = 0; i < MAX_PLAYERS; i++)
11964   {
11965     int actual_player_action = stored_player[i].effective_action;
11966
11967 #if 1
11968     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11969        - rnd_equinox_tetrachloride 048
11970        - rnd_equinox_tetrachloride_ii 096
11971        - rnd_emanuel_schmieg 002
11972        - doctor_sloan_ww 001, 020
11973     */
11974     if (stored_player[i].MovPos == 0)
11975       CheckGravityMovement(&stored_player[i]);
11976 #endif
11977
11978     // overwrite programmed action with tape action
11979     if (stored_player[i].programmed_action)
11980       actual_player_action = stored_player[i].programmed_action;
11981
11982     PlayerActions(&stored_player[i], actual_player_action);
11983
11984     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11985   }
11986
11987   // single step pause mode may already have been toggled by "ScrollPlayer()"
11988   if (game.enter_single_step_mode && !tape.pausing)
11989     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11990
11991   ScrollScreen(NULL, SCROLL_GO_ON);
11992
11993   /* for backwards compatibility, the following code emulates a fixed bug that
11994      occured when pushing elements (causing elements that just made their last
11995      pushing step to already (if possible) make their first falling step in the
11996      same game frame, which is bad); this code is also needed to use the famous
11997      "spring push bug" which is used in older levels and might be wanted to be
11998      used also in newer levels, but in this case the buggy pushing code is only
11999      affecting the "spring" element and no other elements */
12000
12001   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12002   {
12003     for (i = 0; i < MAX_PLAYERS; i++)
12004     {
12005       struct PlayerInfo *player = &stored_player[i];
12006       int x = player->jx;
12007       int y = player->jy;
12008
12009       if (player->active && player->is_pushing && player->is_moving &&
12010           IS_MOVING(x, y) &&
12011           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12012            Tile[x][y] == EL_SPRING))
12013       {
12014         ContinueMoving(x, y);
12015
12016         // continue moving after pushing (this is actually a bug)
12017         if (!IS_MOVING(x, y))
12018           Stop[x][y] = FALSE;
12019       }
12020     }
12021   }
12022
12023   SCAN_PLAYFIELD(x, y)
12024   {
12025     Last[x][y] = Tile[x][y];
12026
12027     ChangeCount[x][y] = 0;
12028     ChangeEvent[x][y] = -1;
12029
12030     // this must be handled before main playfield loop
12031     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12032     {
12033       MovDelay[x][y]--;
12034       if (MovDelay[x][y] <= 0)
12035         RemoveField(x, y);
12036     }
12037
12038     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12039     {
12040       MovDelay[x][y]--;
12041       if (MovDelay[x][y] <= 0)
12042       {
12043         RemoveField(x, y);
12044         TEST_DrawLevelField(x, y);
12045
12046         TestIfElementTouchesCustomElement(x, y);        // for empty space
12047       }
12048     }
12049
12050 #if DEBUG
12051     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12052     {
12053       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12054             x, y);
12055       Debug("game:playing:GameActions_RND", "This should never happen!");
12056
12057       ChangePage[x][y] = -1;
12058     }
12059 #endif
12060
12061     Stop[x][y] = FALSE;
12062     if (WasJustMoving[x][y] > 0)
12063       WasJustMoving[x][y]--;
12064     if (WasJustFalling[x][y] > 0)
12065       WasJustFalling[x][y]--;
12066     if (CheckCollision[x][y] > 0)
12067       CheckCollision[x][y]--;
12068     if (CheckImpact[x][y] > 0)
12069       CheckImpact[x][y]--;
12070
12071     GfxFrame[x][y]++;
12072
12073     /* reset finished pushing action (not done in ContinueMoving() to allow
12074        continuous pushing animation for elements with zero push delay) */
12075     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12076     {
12077       ResetGfxAnimation(x, y);
12078       TEST_DrawLevelField(x, y);
12079     }
12080
12081 #if DEBUG
12082     if (IS_BLOCKED(x, y))
12083     {
12084       int oldx, oldy;
12085
12086       Blocked2Moving(x, y, &oldx, &oldy);
12087       if (!IS_MOVING(oldx, oldy))
12088       {
12089         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12090         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12091         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12092         Debug("game:playing:GameActions_RND", "This should never happen!");
12093       }
12094     }
12095 #endif
12096   }
12097
12098   if (mouse_action.button)
12099   {
12100     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12101
12102     x = mouse_action.lx;
12103     y = mouse_action.ly;
12104     element = Tile[x][y];
12105
12106     if (new_button)
12107     {
12108       CheckElementChange(x, y, element, EL_UNDEFINED, CE_CLICKED_BY_MOUSE);
12109       CheckTriggeredElementChange(x, y, element, CE_MOUSE_CLICKED_ON_X);
12110     }
12111
12112     CheckElementChange(x, y, element, EL_UNDEFINED, CE_PRESSED_BY_MOUSE);
12113     CheckTriggeredElementChange(x, y, element, CE_MOUSE_PRESSED_ON_X);
12114   }
12115
12116   SCAN_PLAYFIELD(x, y)
12117   {
12118     element = Tile[x][y];
12119     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12120     last_gfx_frame = GfxFrame[x][y];
12121
12122     ResetGfxFrame(x, y);
12123
12124     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12125       DrawLevelGraphicAnimation(x, y, graphic);
12126
12127     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12128         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12129       ResetRandomAnimationValue(x, y);
12130
12131     SetRandomAnimationValue(x, y);
12132
12133     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12134
12135     if (IS_INACTIVE(element))
12136     {
12137       if (IS_ANIMATED(graphic))
12138         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12139
12140       continue;
12141     }
12142
12143     // this may take place after moving, so 'element' may have changed
12144     if (IS_CHANGING(x, y) &&
12145         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12146     {
12147       int page = element_info[element].event_page_nr[CE_DELAY];
12148
12149       HandleElementChange(x, y, page);
12150
12151       element = Tile[x][y];
12152       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12153     }
12154
12155     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12156     {
12157       StartMoving(x, y);
12158
12159       element = Tile[x][y];
12160       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12161
12162       if (IS_ANIMATED(graphic) &&
12163           !IS_MOVING(x, y) &&
12164           !Stop[x][y])
12165         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12166
12167       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12168         TEST_DrawTwinkleOnField(x, y);
12169     }
12170     else if (element == EL_ACID)
12171     {
12172       if (!Stop[x][y])
12173         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12174     }
12175     else if ((element == EL_EXIT_OPEN ||
12176               element == EL_EM_EXIT_OPEN ||
12177               element == EL_SP_EXIT_OPEN ||
12178               element == EL_STEEL_EXIT_OPEN ||
12179               element == EL_EM_STEEL_EXIT_OPEN ||
12180               element == EL_SP_TERMINAL ||
12181               element == EL_SP_TERMINAL_ACTIVE ||
12182               element == EL_EXTRA_TIME ||
12183               element == EL_SHIELD_NORMAL ||
12184               element == EL_SHIELD_DEADLY) &&
12185              IS_ANIMATED(graphic))
12186       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12187     else if (IS_MOVING(x, y))
12188       ContinueMoving(x, y);
12189     else if (IS_ACTIVE_BOMB(element))
12190       CheckDynamite(x, y);
12191     else if (element == EL_AMOEBA_GROWING)
12192       AmoebaGrowing(x, y);
12193     else if (element == EL_AMOEBA_SHRINKING)
12194       AmoebaShrinking(x, y);
12195
12196 #if !USE_NEW_AMOEBA_CODE
12197     else if (IS_AMOEBALIVE(element))
12198       AmoebaReproduce(x, y);
12199 #endif
12200
12201     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12202       Life(x, y);
12203     else if (element == EL_EXIT_CLOSED)
12204       CheckExit(x, y);
12205     else if (element == EL_EM_EXIT_CLOSED)
12206       CheckExitEM(x, y);
12207     else if (element == EL_STEEL_EXIT_CLOSED)
12208       CheckExitSteel(x, y);
12209     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12210       CheckExitSteelEM(x, y);
12211     else if (element == EL_SP_EXIT_CLOSED)
12212       CheckExitSP(x, y);
12213     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12214              element == EL_EXPANDABLE_STEELWALL_GROWING)
12215       MauerWaechst(x, y);
12216     else if (element == EL_EXPANDABLE_WALL ||
12217              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12218              element == EL_EXPANDABLE_WALL_VERTICAL ||
12219              element == EL_EXPANDABLE_WALL_ANY ||
12220              element == EL_BD_EXPANDABLE_WALL)
12221       MauerAbleger(x, y);
12222     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12223              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12224              element == EL_EXPANDABLE_STEELWALL_ANY)
12225       MauerAblegerStahl(x, y);
12226     else if (element == EL_FLAMES)
12227       CheckForDragon(x, y);
12228     else if (element == EL_EXPLOSION)
12229       ; // drawing of correct explosion animation is handled separately
12230     else if (element == EL_ELEMENT_SNAPPING ||
12231              element == EL_DIAGONAL_SHRINKING ||
12232              element == EL_DIAGONAL_GROWING)
12233     {
12234       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12235
12236       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12237     }
12238     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12239       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12240
12241     if (IS_BELT_ACTIVE(element))
12242       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12243
12244     if (game.magic_wall_active)
12245     {
12246       int jx = local_player->jx, jy = local_player->jy;
12247
12248       // play the element sound at the position nearest to the player
12249       if ((element == EL_MAGIC_WALL_FULL ||
12250            element == EL_MAGIC_WALL_ACTIVE ||
12251            element == EL_MAGIC_WALL_EMPTYING ||
12252            element == EL_BD_MAGIC_WALL_FULL ||
12253            element == EL_BD_MAGIC_WALL_ACTIVE ||
12254            element == EL_BD_MAGIC_WALL_EMPTYING ||
12255            element == EL_DC_MAGIC_WALL_FULL ||
12256            element == EL_DC_MAGIC_WALL_ACTIVE ||
12257            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12258           ABS(x - jx) + ABS(y - jy) <
12259           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12260       {
12261         magic_wall_x = x;
12262         magic_wall_y = y;
12263       }
12264     }
12265   }
12266
12267 #if USE_NEW_AMOEBA_CODE
12268   // new experimental amoeba growth stuff
12269   if (!(FrameCounter % 8))
12270   {
12271     static unsigned int random = 1684108901;
12272
12273     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12274     {
12275       x = RND(lev_fieldx);
12276       y = RND(lev_fieldy);
12277       element = Tile[x][y];
12278
12279       if (!IS_PLAYER(x,y) &&
12280           (element == EL_EMPTY ||
12281            CAN_GROW_INTO(element) ||
12282            element == EL_QUICKSAND_EMPTY ||
12283            element == EL_QUICKSAND_FAST_EMPTY ||
12284            element == EL_ACID_SPLASH_LEFT ||
12285            element == EL_ACID_SPLASH_RIGHT))
12286       {
12287         if ((IN_LEV_FIELD(x, y-1) && Tile[x][y-1] == EL_AMOEBA_WET) ||
12288             (IN_LEV_FIELD(x-1, y) && Tile[x-1][y] == EL_AMOEBA_WET) ||
12289             (IN_LEV_FIELD(x+1, y) && Tile[x+1][y] == EL_AMOEBA_WET) ||
12290             (IN_LEV_FIELD(x, y+1) && Tile[x][y+1] == EL_AMOEBA_WET))
12291           Tile[x][y] = EL_AMOEBA_DROP;
12292       }
12293
12294       random = random * 129 + 1;
12295     }
12296   }
12297 #endif
12298
12299   game.explosions_delayed = FALSE;
12300
12301   SCAN_PLAYFIELD(x, y)
12302   {
12303     element = Tile[x][y];
12304
12305     if (ExplodeField[x][y])
12306       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12307     else if (element == EL_EXPLOSION)
12308       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12309
12310     ExplodeField[x][y] = EX_TYPE_NONE;
12311   }
12312
12313   game.explosions_delayed = TRUE;
12314
12315   if (game.magic_wall_active)
12316   {
12317     if (!(game.magic_wall_time_left % 4))
12318     {
12319       int element = Tile[magic_wall_x][magic_wall_y];
12320
12321       if (element == EL_BD_MAGIC_WALL_FULL ||
12322           element == EL_BD_MAGIC_WALL_ACTIVE ||
12323           element == EL_BD_MAGIC_WALL_EMPTYING)
12324         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12325       else if (element == EL_DC_MAGIC_WALL_FULL ||
12326                element == EL_DC_MAGIC_WALL_ACTIVE ||
12327                element == EL_DC_MAGIC_WALL_EMPTYING)
12328         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12329       else
12330         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12331     }
12332
12333     if (game.magic_wall_time_left > 0)
12334     {
12335       game.magic_wall_time_left--;
12336
12337       if (!game.magic_wall_time_left)
12338       {
12339         SCAN_PLAYFIELD(x, y)
12340         {
12341           element = Tile[x][y];
12342
12343           if (element == EL_MAGIC_WALL_ACTIVE ||
12344               element == EL_MAGIC_WALL_FULL)
12345           {
12346             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12347             TEST_DrawLevelField(x, y);
12348           }
12349           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12350                    element == EL_BD_MAGIC_WALL_FULL)
12351           {
12352             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12353             TEST_DrawLevelField(x, y);
12354           }
12355           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12356                    element == EL_DC_MAGIC_WALL_FULL)
12357           {
12358             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12359             TEST_DrawLevelField(x, y);
12360           }
12361         }
12362
12363         game.magic_wall_active = FALSE;
12364       }
12365     }
12366   }
12367
12368   if (game.light_time_left > 0)
12369   {
12370     game.light_time_left--;
12371
12372     if (game.light_time_left == 0)
12373       RedrawAllLightSwitchesAndInvisibleElements();
12374   }
12375
12376   if (game.timegate_time_left > 0)
12377   {
12378     game.timegate_time_left--;
12379
12380     if (game.timegate_time_left == 0)
12381       CloseAllOpenTimegates();
12382   }
12383
12384   if (game.lenses_time_left > 0)
12385   {
12386     game.lenses_time_left--;
12387
12388     if (game.lenses_time_left == 0)
12389       RedrawAllInvisibleElementsForLenses();
12390   }
12391
12392   if (game.magnify_time_left > 0)
12393   {
12394     game.magnify_time_left--;
12395
12396     if (game.magnify_time_left == 0)
12397       RedrawAllInvisibleElementsForMagnifier();
12398   }
12399
12400   for (i = 0; i < MAX_PLAYERS; i++)
12401   {
12402     struct PlayerInfo *player = &stored_player[i];
12403
12404     if (SHIELD_ON(player))
12405     {
12406       if (player->shield_deadly_time_left)
12407         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12408       else if (player->shield_normal_time_left)
12409         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12410     }
12411   }
12412
12413 #if USE_DELAYED_GFX_REDRAW
12414   SCAN_PLAYFIELD(x, y)
12415   {
12416     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12417     {
12418       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12419          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12420
12421       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12422         DrawLevelField(x, y);
12423
12424       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12425         DrawLevelFieldCrumbled(x, y);
12426
12427       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12428         DrawLevelFieldCrumbledNeighbours(x, y);
12429
12430       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12431         DrawTwinkleOnField(x, y);
12432     }
12433
12434     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12435   }
12436 #endif
12437
12438   DrawAllPlayers();
12439   PlayAllPlayersSound();
12440
12441   for (i = 0; i < MAX_PLAYERS; i++)
12442   {
12443     struct PlayerInfo *player = &stored_player[i];
12444
12445     if (player->show_envelope != 0 && (!player->active ||
12446                                        player->MovPos == 0))
12447     {
12448       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12449
12450       player->show_envelope = 0;
12451     }
12452   }
12453
12454   // use random number generator in every frame to make it less predictable
12455   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12456     RND(1);
12457
12458   mouse_action_last = mouse_action;
12459 }
12460
12461 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12462 {
12463   int min_x = x, min_y = y, max_x = x, max_y = y;
12464   int scr_fieldx = getScreenFieldSizeX();
12465   int scr_fieldy = getScreenFieldSizeY();
12466   int i;
12467
12468   for (i = 0; i < MAX_PLAYERS; i++)
12469   {
12470     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12471
12472     if (!stored_player[i].active || &stored_player[i] == player)
12473       continue;
12474
12475     min_x = MIN(min_x, jx);
12476     min_y = MIN(min_y, jy);
12477     max_x = MAX(max_x, jx);
12478     max_y = MAX(max_y, jy);
12479   }
12480
12481   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12482 }
12483
12484 static boolean AllPlayersInVisibleScreen(void)
12485 {
12486   int i;
12487
12488   for (i = 0; i < MAX_PLAYERS; i++)
12489   {
12490     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12491
12492     if (!stored_player[i].active)
12493       continue;
12494
12495     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12496       return FALSE;
12497   }
12498
12499   return TRUE;
12500 }
12501
12502 void ScrollLevel(int dx, int dy)
12503 {
12504   int scroll_offset = 2 * TILEX_VAR;
12505   int x, y;
12506
12507   BlitBitmap(drawto_field, drawto_field,
12508              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12509              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12510              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12511              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12512              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12513              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12514
12515   if (dx != 0)
12516   {
12517     x = (dx == 1 ? BX1 : BX2);
12518     for (y = BY1; y <= BY2; y++)
12519       DrawScreenField(x, y);
12520   }
12521
12522   if (dy != 0)
12523   {
12524     y = (dy == 1 ? BY1 : BY2);
12525     for (x = BX1; x <= BX2; x++)
12526       DrawScreenField(x, y);
12527   }
12528
12529   redraw_mask |= REDRAW_FIELD;
12530 }
12531
12532 static boolean canFallDown(struct PlayerInfo *player)
12533 {
12534   int jx = player->jx, jy = player->jy;
12535
12536   return (IN_LEV_FIELD(jx, jy + 1) &&
12537           (IS_FREE(jx, jy + 1) ||
12538            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12539           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12540           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12541 }
12542
12543 static boolean canPassField(int x, int y, int move_dir)
12544 {
12545   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12546   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12547   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12548   int nextx = x + dx;
12549   int nexty = y + dy;
12550   int element = Tile[x][y];
12551
12552   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12553           !CAN_MOVE(element) &&
12554           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12555           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12556           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12557 }
12558
12559 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12560 {
12561   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12562   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12563   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12564   int newx = x + dx;
12565   int newy = y + dy;
12566
12567   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12568           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12569           (IS_DIGGABLE(Tile[newx][newy]) ||
12570            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12571            canPassField(newx, newy, move_dir)));
12572 }
12573
12574 static void CheckGravityMovement(struct PlayerInfo *player)
12575 {
12576   if (player->gravity && !player->programmed_action)
12577   {
12578     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12579     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12580     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12581     int jx = player->jx, jy = player->jy;
12582     boolean player_is_moving_to_valid_field =
12583       (!player_is_snapping &&
12584        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12585         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12586     boolean player_can_fall_down = canFallDown(player);
12587
12588     if (player_can_fall_down &&
12589         !player_is_moving_to_valid_field)
12590       player->programmed_action = MV_DOWN;
12591   }
12592 }
12593
12594 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12595 {
12596   return CheckGravityMovement(player);
12597
12598   if (player->gravity && !player->programmed_action)
12599   {
12600     int jx = player->jx, jy = player->jy;
12601     boolean field_under_player_is_free =
12602       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12603     boolean player_is_standing_on_valid_field =
12604       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12605        (IS_WALKABLE(Tile[jx][jy]) &&
12606         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12607
12608     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12609       player->programmed_action = MV_DOWN;
12610   }
12611 }
12612
12613 /*
12614   MovePlayerOneStep()
12615   -----------------------------------------------------------------------------
12616   dx, dy:               direction (non-diagonal) to try to move the player to
12617   real_dx, real_dy:     direction as read from input device (can be diagonal)
12618 */
12619
12620 boolean MovePlayerOneStep(struct PlayerInfo *player,
12621                           int dx, int dy, int real_dx, int real_dy)
12622 {
12623   int jx = player->jx, jy = player->jy;
12624   int new_jx = jx + dx, new_jy = jy + dy;
12625   int can_move;
12626   boolean player_can_move = !player->cannot_move;
12627
12628   if (!player->active || (!dx && !dy))
12629     return MP_NO_ACTION;
12630
12631   player->MovDir = (dx < 0 ? MV_LEFT :
12632                     dx > 0 ? MV_RIGHT :
12633                     dy < 0 ? MV_UP :
12634                     dy > 0 ? MV_DOWN :  MV_NONE);
12635
12636   if (!IN_LEV_FIELD(new_jx, new_jy))
12637     return MP_NO_ACTION;
12638
12639   if (!player_can_move)
12640   {
12641     if (player->MovPos == 0)
12642     {
12643       player->is_moving = FALSE;
12644       player->is_digging = FALSE;
12645       player->is_collecting = FALSE;
12646       player->is_snapping = FALSE;
12647       player->is_pushing = FALSE;
12648     }
12649   }
12650
12651   if (!network.enabled && game.centered_player_nr == -1 &&
12652       !AllPlayersInSight(player, new_jx, new_jy))
12653     return MP_NO_ACTION;
12654
12655   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12656   if (can_move != MP_MOVING)
12657     return can_move;
12658
12659   // check if DigField() has caused relocation of the player
12660   if (player->jx != jx || player->jy != jy)
12661     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12662
12663   StorePlayer[jx][jy] = 0;
12664   player->last_jx = jx;
12665   player->last_jy = jy;
12666   player->jx = new_jx;
12667   player->jy = new_jy;
12668   StorePlayer[new_jx][new_jy] = player->element_nr;
12669
12670   if (player->move_delay_value_next != -1)
12671   {
12672     player->move_delay_value = player->move_delay_value_next;
12673     player->move_delay_value_next = -1;
12674   }
12675
12676   player->MovPos =
12677     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12678
12679   player->step_counter++;
12680
12681   PlayerVisit[jx][jy] = FrameCounter;
12682
12683   player->is_moving = TRUE;
12684
12685 #if 1
12686   // should better be called in MovePlayer(), but this breaks some tapes
12687   ScrollPlayer(player, SCROLL_INIT);
12688 #endif
12689
12690   return MP_MOVING;
12691 }
12692
12693 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12694 {
12695   int jx = player->jx, jy = player->jy;
12696   int old_jx = jx, old_jy = jy;
12697   int moved = MP_NO_ACTION;
12698
12699   if (!player->active)
12700     return FALSE;
12701
12702   if (!dx && !dy)
12703   {
12704     if (player->MovPos == 0)
12705     {
12706       player->is_moving = FALSE;
12707       player->is_digging = FALSE;
12708       player->is_collecting = FALSE;
12709       player->is_snapping = FALSE;
12710       player->is_pushing = FALSE;
12711     }
12712
12713     return FALSE;
12714   }
12715
12716   if (player->move_delay > 0)
12717     return FALSE;
12718
12719   player->move_delay = -1;              // set to "uninitialized" value
12720
12721   // store if player is automatically moved to next field
12722   player->is_auto_moving = (player->programmed_action != MV_NONE);
12723
12724   // remove the last programmed player action
12725   player->programmed_action = 0;
12726
12727   if (player->MovPos)
12728   {
12729     // should only happen if pre-1.2 tape recordings are played
12730     // this is only for backward compatibility
12731
12732     int original_move_delay_value = player->move_delay_value;
12733
12734 #if DEBUG
12735     Debug("game:playing:MovePlayer",
12736           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12737           tape.counter);
12738 #endif
12739
12740     // scroll remaining steps with finest movement resolution
12741     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12742
12743     while (player->MovPos)
12744     {
12745       ScrollPlayer(player, SCROLL_GO_ON);
12746       ScrollScreen(NULL, SCROLL_GO_ON);
12747
12748       AdvanceFrameAndPlayerCounters(player->index_nr);
12749
12750       DrawAllPlayers();
12751       BackToFront_WithFrameDelay(0);
12752     }
12753
12754     player->move_delay_value = original_move_delay_value;
12755   }
12756
12757   player->is_active = FALSE;
12758
12759   if (player->last_move_dir & MV_HORIZONTAL)
12760   {
12761     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12762       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12763   }
12764   else
12765   {
12766     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12767       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12768   }
12769
12770   if (!moved && !player->is_active)
12771   {
12772     player->is_moving = FALSE;
12773     player->is_digging = FALSE;
12774     player->is_collecting = FALSE;
12775     player->is_snapping = FALSE;
12776     player->is_pushing = FALSE;
12777   }
12778
12779   jx = player->jx;
12780   jy = player->jy;
12781
12782   if (moved & MP_MOVING && !ScreenMovPos &&
12783       (player->index_nr == game.centered_player_nr ||
12784        game.centered_player_nr == -1))
12785   {
12786     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12787
12788     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12789     {
12790       // actual player has left the screen -- scroll in that direction
12791       if (jx != old_jx)         // player has moved horizontally
12792         scroll_x += (jx - old_jx);
12793       else                      // player has moved vertically
12794         scroll_y += (jy - old_jy);
12795     }
12796     else
12797     {
12798       int offset_raw = game.scroll_delay_value;
12799
12800       if (jx != old_jx)         // player has moved horizontally
12801       {
12802         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12803         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12804         int new_scroll_x = jx - MIDPOSX + offset_x;
12805
12806         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12807             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12808           scroll_x = new_scroll_x;
12809
12810         // don't scroll over playfield boundaries
12811         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12812
12813         // don't scroll more than one field at a time
12814         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12815
12816         // don't scroll against the player's moving direction
12817         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12818             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12819           scroll_x = old_scroll_x;
12820       }
12821       else                      // player has moved vertically
12822       {
12823         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12824         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12825         int new_scroll_y = jy - MIDPOSY + offset_y;
12826
12827         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12828             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12829           scroll_y = new_scroll_y;
12830
12831         // don't scroll over playfield boundaries
12832         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
12833
12834         // don't scroll more than one field at a time
12835         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12836
12837         // don't scroll against the player's moving direction
12838         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12839             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12840           scroll_y = old_scroll_y;
12841       }
12842     }
12843
12844     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12845     {
12846       if (!network.enabled && game.centered_player_nr == -1 &&
12847           !AllPlayersInVisibleScreen())
12848       {
12849         scroll_x = old_scroll_x;
12850         scroll_y = old_scroll_y;
12851       }
12852       else
12853       {
12854         ScrollScreen(player, SCROLL_INIT);
12855         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12856       }
12857     }
12858   }
12859
12860   player->StepFrame = 0;
12861
12862   if (moved & MP_MOVING)
12863   {
12864     if (old_jx != jx && old_jy == jy)
12865       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12866     else if (old_jx == jx && old_jy != jy)
12867       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12868
12869     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12870
12871     player->last_move_dir = player->MovDir;
12872     player->is_moving = TRUE;
12873     player->is_snapping = FALSE;
12874     player->is_switching = FALSE;
12875     player->is_dropping = FALSE;
12876     player->is_dropping_pressed = FALSE;
12877     player->drop_pressed_delay = 0;
12878
12879 #if 0
12880     // should better be called here than above, but this breaks some tapes
12881     ScrollPlayer(player, SCROLL_INIT);
12882 #endif
12883   }
12884   else
12885   {
12886     CheckGravityMovementWhenNotMoving(player);
12887
12888     player->is_moving = FALSE;
12889
12890     /* at this point, the player is allowed to move, but cannot move right now
12891        (e.g. because of something blocking the way) -- ensure that the player
12892        is also allowed to move in the next frame (in old versions before 3.1.1,
12893        the player was forced to wait again for eight frames before next try) */
12894
12895     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12896       player->move_delay = 0;   // allow direct movement in the next frame
12897   }
12898
12899   if (player->move_delay == -1)         // not yet initialized by DigField()
12900     player->move_delay = player->move_delay_value;
12901
12902   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12903   {
12904     TestIfPlayerTouchesBadThing(jx, jy);
12905     TestIfPlayerTouchesCustomElement(jx, jy);
12906   }
12907
12908   if (!player->active)
12909     RemovePlayer(player);
12910
12911   return moved;
12912 }
12913
12914 void ScrollPlayer(struct PlayerInfo *player, int mode)
12915 {
12916   int jx = player->jx, jy = player->jy;
12917   int last_jx = player->last_jx, last_jy = player->last_jy;
12918   int move_stepsize = TILEX / player->move_delay_value;
12919
12920   if (!player->active)
12921     return;
12922
12923   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
12924     return;
12925
12926   if (mode == SCROLL_INIT)
12927   {
12928     player->actual_frame_counter = FrameCounter;
12929     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12930
12931     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12932         Tile[last_jx][last_jy] == EL_EMPTY)
12933     {
12934       int last_field_block_delay = 0;   // start with no blocking at all
12935       int block_delay_adjustment = player->block_delay_adjustment;
12936
12937       // if player blocks last field, add delay for exactly one move
12938       if (player->block_last_field)
12939       {
12940         last_field_block_delay += player->move_delay_value;
12941
12942         // when blocking enabled, prevent moving up despite gravity
12943         if (player->gravity && player->MovDir == MV_UP)
12944           block_delay_adjustment = -1;
12945       }
12946
12947       // add block delay adjustment (also possible when not blocking)
12948       last_field_block_delay += block_delay_adjustment;
12949
12950       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12951       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12952     }
12953
12954     if (player->MovPos != 0)    // player has not yet reached destination
12955       return;
12956   }
12957   else if (!FrameReached(&player->actual_frame_counter, 1))
12958     return;
12959
12960   if (player->MovPos != 0)
12961   {
12962     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12963     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12964
12965     // before DrawPlayer() to draw correct player graphic for this case
12966     if (player->MovPos == 0)
12967       CheckGravityMovement(player);
12968   }
12969
12970   if (player->MovPos == 0)      // player reached destination field
12971   {
12972     if (player->move_delay_reset_counter > 0)
12973     {
12974       player->move_delay_reset_counter--;
12975
12976       if (player->move_delay_reset_counter == 0)
12977       {
12978         // continue with normal speed after quickly moving through gate
12979         HALVE_PLAYER_SPEED(player);
12980
12981         // be able to make the next move without delay
12982         player->move_delay = 0;
12983       }
12984     }
12985
12986     player->last_jx = jx;
12987     player->last_jy = jy;
12988
12989     if (Tile[jx][jy] == EL_EXIT_OPEN ||
12990         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
12991         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
12992         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
12993         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12994         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12995         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
12996         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
12997     {
12998       ExitPlayer(player);
12999
13000       if (game.players_still_needed == 0 &&
13001           (game.friends_still_needed == 0 ||
13002            IS_SP_ELEMENT(Tile[jx][jy])))
13003         LevelSolved();
13004     }
13005
13006     // this breaks one level: "machine", level 000
13007     {
13008       int move_direction = player->MovDir;
13009       int enter_side = MV_DIR_OPPOSITE(move_direction);
13010       int leave_side = move_direction;
13011       int old_jx = last_jx;
13012       int old_jy = last_jy;
13013       int old_element = Tile[old_jx][old_jy];
13014       int new_element = Tile[jx][jy];
13015
13016       if (IS_CUSTOM_ELEMENT(old_element))
13017         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13018                                    CE_LEFT_BY_PLAYER,
13019                                    player->index_bit, leave_side);
13020
13021       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13022                                           CE_PLAYER_LEAVES_X,
13023                                           player->index_bit, leave_side);
13024
13025       if (IS_CUSTOM_ELEMENT(new_element))
13026         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13027                                    player->index_bit, enter_side);
13028
13029       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13030                                           CE_PLAYER_ENTERS_X,
13031                                           player->index_bit, enter_side);
13032
13033       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13034                                         CE_MOVE_OF_X, move_direction);
13035     }
13036
13037     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13038     {
13039       TestIfPlayerTouchesBadThing(jx, jy);
13040       TestIfPlayerTouchesCustomElement(jx, jy);
13041
13042       /* needed because pushed element has not yet reached its destination,
13043          so it would trigger a change event at its previous field location */
13044       if (!player->is_pushing)
13045         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13046
13047       if (!player->active)
13048         RemovePlayer(player);
13049     }
13050
13051     if (!game.LevelSolved && level.use_step_counter)
13052     {
13053       int i;
13054
13055       TimePlayed++;
13056
13057       if (TimeLeft > 0)
13058       {
13059         TimeLeft--;
13060
13061         if (TimeLeft <= 10 && setup.time_limit)
13062           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13063
13064         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13065
13066         DisplayGameControlValues();
13067
13068         if (!TimeLeft && setup.time_limit)
13069           for (i = 0; i < MAX_PLAYERS; i++)
13070             KillPlayer(&stored_player[i]);
13071       }
13072       else if (game.no_time_limit && !game.all_players_gone)
13073       {
13074         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13075
13076         DisplayGameControlValues();
13077       }
13078     }
13079
13080     if (tape.single_step && tape.recording && !tape.pausing &&
13081         !player->programmed_action)
13082       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13083
13084     if (!player->programmed_action)
13085       CheckSaveEngineSnapshot(player);
13086   }
13087 }
13088
13089 void ScrollScreen(struct PlayerInfo *player, int mode)
13090 {
13091   static unsigned int screen_frame_counter = 0;
13092
13093   if (mode == SCROLL_INIT)
13094   {
13095     // set scrolling step size according to actual player's moving speed
13096     ScrollStepSize = TILEX / player->move_delay_value;
13097
13098     screen_frame_counter = FrameCounter;
13099     ScreenMovDir = player->MovDir;
13100     ScreenMovPos = player->MovPos;
13101     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13102     return;
13103   }
13104   else if (!FrameReached(&screen_frame_counter, 1))
13105     return;
13106
13107   if (ScreenMovPos)
13108   {
13109     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13110     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13111     redraw_mask |= REDRAW_FIELD;
13112   }
13113   else
13114     ScreenMovDir = MV_NONE;
13115 }
13116
13117 void TestIfPlayerTouchesCustomElement(int x, int y)
13118 {
13119   static int xy[4][2] =
13120   {
13121     { 0, -1 },
13122     { -1, 0 },
13123     { +1, 0 },
13124     { 0, +1 }
13125   };
13126   static int trigger_sides[4][2] =
13127   {
13128     // center side       border side
13129     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13130     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13131     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13132     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13133   };
13134   static int touch_dir[4] =
13135   {
13136     MV_LEFT | MV_RIGHT,
13137     MV_UP   | MV_DOWN,
13138     MV_UP   | MV_DOWN,
13139     MV_LEFT | MV_RIGHT
13140   };
13141   int center_element = Tile[x][y];      // should always be non-moving!
13142   int i;
13143
13144   for (i = 0; i < NUM_DIRECTIONS; i++)
13145   {
13146     int xx = x + xy[i][0];
13147     int yy = y + xy[i][1];
13148     int center_side = trigger_sides[i][0];
13149     int border_side = trigger_sides[i][1];
13150     int border_element;
13151
13152     if (!IN_LEV_FIELD(xx, yy))
13153       continue;
13154
13155     if (IS_PLAYER(x, y))                // player found at center element
13156     {
13157       struct PlayerInfo *player = PLAYERINFO(x, y);
13158
13159       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13160         border_element = Tile[xx][yy];          // may be moving!
13161       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13162         border_element = Tile[xx][yy];
13163       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13164         border_element = MovingOrBlocked2Element(xx, yy);
13165       else
13166         continue;               // center and border element do not touch
13167
13168       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13169                                  player->index_bit, border_side);
13170       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13171                                           CE_PLAYER_TOUCHES_X,
13172                                           player->index_bit, border_side);
13173
13174       {
13175         /* use player element that is initially defined in the level playfield,
13176            not the player element that corresponds to the runtime player number
13177            (example: a level that contains EL_PLAYER_3 as the only player would
13178            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13179         int player_element = PLAYERINFO(x, y)->initial_element;
13180
13181         CheckElementChangeBySide(xx, yy, border_element, player_element,
13182                                  CE_TOUCHING_X, border_side);
13183       }
13184     }
13185     else if (IS_PLAYER(xx, yy))         // player found at border element
13186     {
13187       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13188
13189       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13190       {
13191         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13192           continue;             // center and border element do not touch
13193       }
13194
13195       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13196                                  player->index_bit, center_side);
13197       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13198                                           CE_PLAYER_TOUCHES_X,
13199                                           player->index_bit, center_side);
13200
13201       {
13202         /* use player element that is initially defined in the level playfield,
13203            not the player element that corresponds to the runtime player number
13204            (example: a level that contains EL_PLAYER_3 as the only player would
13205            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13206         int player_element = PLAYERINFO(xx, yy)->initial_element;
13207
13208         CheckElementChangeBySide(x, y, center_element, player_element,
13209                                  CE_TOUCHING_X, center_side);
13210       }
13211
13212       break;
13213     }
13214   }
13215 }
13216
13217 void TestIfElementTouchesCustomElement(int x, int y)
13218 {
13219   static int xy[4][2] =
13220   {
13221     { 0, -1 },
13222     { -1, 0 },
13223     { +1, 0 },
13224     { 0, +1 }
13225   };
13226   static int trigger_sides[4][2] =
13227   {
13228     // center side      border side
13229     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13230     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13231     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13232     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13233   };
13234   static int touch_dir[4] =
13235   {
13236     MV_LEFT | MV_RIGHT,
13237     MV_UP   | MV_DOWN,
13238     MV_UP   | MV_DOWN,
13239     MV_LEFT | MV_RIGHT
13240   };
13241   boolean change_center_element = FALSE;
13242   int center_element = Tile[x][y];      // should always be non-moving!
13243   int border_element_old[NUM_DIRECTIONS];
13244   int i;
13245
13246   for (i = 0; i < NUM_DIRECTIONS; i++)
13247   {
13248     int xx = x + xy[i][0];
13249     int yy = y + xy[i][1];
13250     int border_element;
13251
13252     border_element_old[i] = -1;
13253
13254     if (!IN_LEV_FIELD(xx, yy))
13255       continue;
13256
13257     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13258       border_element = Tile[xx][yy];    // may be moving!
13259     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13260       border_element = Tile[xx][yy];
13261     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13262       border_element = MovingOrBlocked2Element(xx, yy);
13263     else
13264       continue;                 // center and border element do not touch
13265
13266     border_element_old[i] = border_element;
13267   }
13268
13269   for (i = 0; i < NUM_DIRECTIONS; i++)
13270   {
13271     int xx = x + xy[i][0];
13272     int yy = y + xy[i][1];
13273     int center_side = trigger_sides[i][0];
13274     int border_element = border_element_old[i];
13275
13276     if (border_element == -1)
13277       continue;
13278
13279     // check for change of border element
13280     CheckElementChangeBySide(xx, yy, border_element, center_element,
13281                              CE_TOUCHING_X, center_side);
13282
13283     // (center element cannot be player, so we dont have to check this here)
13284   }
13285
13286   for (i = 0; i < NUM_DIRECTIONS; i++)
13287   {
13288     int xx = x + xy[i][0];
13289     int yy = y + xy[i][1];
13290     int border_side = trigger_sides[i][1];
13291     int border_element = border_element_old[i];
13292
13293     if (border_element == -1)
13294       continue;
13295
13296     // check for change of center element (but change it only once)
13297     if (!change_center_element)
13298       change_center_element =
13299         CheckElementChangeBySide(x, y, center_element, border_element,
13300                                  CE_TOUCHING_X, border_side);
13301
13302     if (IS_PLAYER(xx, yy))
13303     {
13304       /* use player element that is initially defined in the level playfield,
13305          not the player element that corresponds to the runtime player number
13306          (example: a level that contains EL_PLAYER_3 as the only player would
13307          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13308       int player_element = PLAYERINFO(xx, yy)->initial_element;
13309
13310       CheckElementChangeBySide(x, y, center_element, player_element,
13311                                CE_TOUCHING_X, border_side);
13312     }
13313   }
13314 }
13315
13316 void TestIfElementHitsCustomElement(int x, int y, int direction)
13317 {
13318   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13319   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13320   int hitx = x + dx, hity = y + dy;
13321   int hitting_element = Tile[x][y];
13322   int touched_element;
13323
13324   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13325     return;
13326
13327   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13328                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13329
13330   if (IN_LEV_FIELD(hitx, hity))
13331   {
13332     int opposite_direction = MV_DIR_OPPOSITE(direction);
13333     int hitting_side = direction;
13334     int touched_side = opposite_direction;
13335     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13336                           MovDir[hitx][hity] != direction ||
13337                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13338
13339     object_hit = TRUE;
13340
13341     if (object_hit)
13342     {
13343       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13344                                CE_HITTING_X, touched_side);
13345
13346       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13347                                CE_HIT_BY_X, hitting_side);
13348
13349       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13350                                CE_HIT_BY_SOMETHING, opposite_direction);
13351
13352       if (IS_PLAYER(hitx, hity))
13353       {
13354         /* use player element that is initially defined in the level playfield,
13355            not the player element that corresponds to the runtime player number
13356            (example: a level that contains EL_PLAYER_3 as the only player would
13357            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13358         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13359
13360         CheckElementChangeBySide(x, y, hitting_element, player_element,
13361                                  CE_HITTING_X, touched_side);
13362       }
13363     }
13364   }
13365
13366   // "hitting something" is also true when hitting the playfield border
13367   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13368                            CE_HITTING_SOMETHING, direction);
13369 }
13370
13371 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13372 {
13373   int i, kill_x = -1, kill_y = -1;
13374
13375   int bad_element = -1;
13376   static int test_xy[4][2] =
13377   {
13378     { 0, -1 },
13379     { -1, 0 },
13380     { +1, 0 },
13381     { 0, +1 }
13382   };
13383   static int test_dir[4] =
13384   {
13385     MV_UP,
13386     MV_LEFT,
13387     MV_RIGHT,
13388     MV_DOWN
13389   };
13390
13391   for (i = 0; i < NUM_DIRECTIONS; i++)
13392   {
13393     int test_x, test_y, test_move_dir, test_element;
13394
13395     test_x = good_x + test_xy[i][0];
13396     test_y = good_y + test_xy[i][1];
13397
13398     if (!IN_LEV_FIELD(test_x, test_y))
13399       continue;
13400
13401     test_move_dir =
13402       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13403
13404     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13405
13406     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13407        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13408     */
13409     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13410         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13411     {
13412       kill_x = test_x;
13413       kill_y = test_y;
13414       bad_element = test_element;
13415
13416       break;
13417     }
13418   }
13419
13420   if (kill_x != -1 || kill_y != -1)
13421   {
13422     if (IS_PLAYER(good_x, good_y))
13423     {
13424       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13425
13426       if (player->shield_deadly_time_left > 0 &&
13427           !IS_INDESTRUCTIBLE(bad_element))
13428         Bang(kill_x, kill_y);
13429       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13430         KillPlayer(player);
13431     }
13432     else
13433       Bang(good_x, good_y);
13434   }
13435 }
13436
13437 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13438 {
13439   int i, kill_x = -1, kill_y = -1;
13440   int bad_element = Tile[bad_x][bad_y];
13441   static int test_xy[4][2] =
13442   {
13443     { 0, -1 },
13444     { -1, 0 },
13445     { +1, 0 },
13446     { 0, +1 }
13447   };
13448   static int touch_dir[4] =
13449   {
13450     MV_LEFT | MV_RIGHT,
13451     MV_UP   | MV_DOWN,
13452     MV_UP   | MV_DOWN,
13453     MV_LEFT | MV_RIGHT
13454   };
13455   static int test_dir[4] =
13456   {
13457     MV_UP,
13458     MV_LEFT,
13459     MV_RIGHT,
13460     MV_DOWN
13461   };
13462
13463   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13464     return;
13465
13466   for (i = 0; i < NUM_DIRECTIONS; i++)
13467   {
13468     int test_x, test_y, test_move_dir, test_element;
13469
13470     test_x = bad_x + test_xy[i][0];
13471     test_y = bad_y + test_xy[i][1];
13472
13473     if (!IN_LEV_FIELD(test_x, test_y))
13474       continue;
13475
13476     test_move_dir =
13477       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13478
13479     test_element = Tile[test_x][test_y];
13480
13481     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13482        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13483     */
13484     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13485         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13486     {
13487       // good thing is player or penguin that does not move away
13488       if (IS_PLAYER(test_x, test_y))
13489       {
13490         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13491
13492         if (bad_element == EL_ROBOT && player->is_moving)
13493           continue;     // robot does not kill player if he is moving
13494
13495         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13496         {
13497           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13498             continue;           // center and border element do not touch
13499         }
13500
13501         kill_x = test_x;
13502         kill_y = test_y;
13503
13504         break;
13505       }
13506       else if (test_element == EL_PENGUIN)
13507       {
13508         kill_x = test_x;
13509         kill_y = test_y;
13510
13511         break;
13512       }
13513     }
13514   }
13515
13516   if (kill_x != -1 || kill_y != -1)
13517   {
13518     if (IS_PLAYER(kill_x, kill_y))
13519     {
13520       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13521
13522       if (player->shield_deadly_time_left > 0 &&
13523           !IS_INDESTRUCTIBLE(bad_element))
13524         Bang(bad_x, bad_y);
13525       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13526         KillPlayer(player);
13527     }
13528     else
13529       Bang(kill_x, kill_y);
13530   }
13531 }
13532
13533 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13534 {
13535   int bad_element = Tile[bad_x][bad_y];
13536   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13537   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13538   int test_x = bad_x + dx, test_y = bad_y + dy;
13539   int test_move_dir, test_element;
13540   int kill_x = -1, kill_y = -1;
13541
13542   if (!IN_LEV_FIELD(test_x, test_y))
13543     return;
13544
13545   test_move_dir =
13546     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13547
13548   test_element = Tile[test_x][test_y];
13549
13550   if (test_move_dir != bad_move_dir)
13551   {
13552     // good thing can be player or penguin that does not move away
13553     if (IS_PLAYER(test_x, test_y))
13554     {
13555       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13556
13557       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13558          player as being hit when he is moving towards the bad thing, because
13559          the "get hit by" condition would be lost after the player stops) */
13560       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13561         return;         // player moves away from bad thing
13562
13563       kill_x = test_x;
13564       kill_y = test_y;
13565     }
13566     else if (test_element == EL_PENGUIN)
13567     {
13568       kill_x = test_x;
13569       kill_y = test_y;
13570     }
13571   }
13572
13573   if (kill_x != -1 || kill_y != -1)
13574   {
13575     if (IS_PLAYER(kill_x, kill_y))
13576     {
13577       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13578
13579       if (player->shield_deadly_time_left > 0 &&
13580           !IS_INDESTRUCTIBLE(bad_element))
13581         Bang(bad_x, bad_y);
13582       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13583         KillPlayer(player);
13584     }
13585     else
13586       Bang(kill_x, kill_y);
13587   }
13588 }
13589
13590 void TestIfPlayerTouchesBadThing(int x, int y)
13591 {
13592   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13593 }
13594
13595 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13596 {
13597   TestIfGoodThingHitsBadThing(x, y, move_dir);
13598 }
13599
13600 void TestIfBadThingTouchesPlayer(int x, int y)
13601 {
13602   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13603 }
13604
13605 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13606 {
13607   TestIfBadThingHitsGoodThing(x, y, move_dir);
13608 }
13609
13610 void TestIfFriendTouchesBadThing(int x, int y)
13611 {
13612   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13613 }
13614
13615 void TestIfBadThingTouchesFriend(int x, int y)
13616 {
13617   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13618 }
13619
13620 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13621 {
13622   int i, kill_x = bad_x, kill_y = bad_y;
13623   static int xy[4][2] =
13624   {
13625     { 0, -1 },
13626     { -1, 0 },
13627     { +1, 0 },
13628     { 0, +1 }
13629   };
13630
13631   for (i = 0; i < NUM_DIRECTIONS; i++)
13632   {
13633     int x, y, element;
13634
13635     x = bad_x + xy[i][0];
13636     y = bad_y + xy[i][1];
13637     if (!IN_LEV_FIELD(x, y))
13638       continue;
13639
13640     element = Tile[x][y];
13641     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13642         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13643     {
13644       kill_x = x;
13645       kill_y = y;
13646       break;
13647     }
13648   }
13649
13650   if (kill_x != bad_x || kill_y != bad_y)
13651     Bang(bad_x, bad_y);
13652 }
13653
13654 void KillPlayer(struct PlayerInfo *player)
13655 {
13656   int jx = player->jx, jy = player->jy;
13657
13658   if (!player->active)
13659     return;
13660
13661 #if 0
13662   Debug("game:playing:KillPlayer",
13663         "0: killed == %d, active == %d, reanimated == %d",
13664         player->killed, player->active, player->reanimated);
13665 #endif
13666
13667   /* the following code was introduced to prevent an infinite loop when calling
13668      -> Bang()
13669      -> CheckTriggeredElementChangeExt()
13670      -> ExecuteCustomElementAction()
13671      -> KillPlayer()
13672      -> (infinitely repeating the above sequence of function calls)
13673      which occurs when killing the player while having a CE with the setting
13674      "kill player X when explosion of <player X>"; the solution using a new
13675      field "player->killed" was chosen for backwards compatibility, although
13676      clever use of the fields "player->active" etc. would probably also work */
13677 #if 1
13678   if (player->killed)
13679     return;
13680 #endif
13681
13682   player->killed = TRUE;
13683
13684   // remove accessible field at the player's position
13685   Tile[jx][jy] = EL_EMPTY;
13686
13687   // deactivate shield (else Bang()/Explode() would not work right)
13688   player->shield_normal_time_left = 0;
13689   player->shield_deadly_time_left = 0;
13690
13691 #if 0
13692   Debug("game:playing:KillPlayer",
13693         "1: killed == %d, active == %d, reanimated == %d",
13694         player->killed, player->active, player->reanimated);
13695 #endif
13696
13697   Bang(jx, jy);
13698
13699 #if 0
13700   Debug("game:playing:KillPlayer",
13701         "2: killed == %d, active == %d, reanimated == %d",
13702         player->killed, player->active, player->reanimated);
13703 #endif
13704
13705   if (player->reanimated)       // killed player may have been reanimated
13706     player->killed = player->reanimated = FALSE;
13707   else
13708     BuryPlayer(player);
13709 }
13710
13711 static void KillPlayerUnlessEnemyProtected(int x, int y)
13712 {
13713   if (!PLAYER_ENEMY_PROTECTED(x, y))
13714     KillPlayer(PLAYERINFO(x, y));
13715 }
13716
13717 static void KillPlayerUnlessExplosionProtected(int x, int y)
13718 {
13719   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13720     KillPlayer(PLAYERINFO(x, y));
13721 }
13722
13723 void BuryPlayer(struct PlayerInfo *player)
13724 {
13725   int jx = player->jx, jy = player->jy;
13726
13727   if (!player->active)
13728     return;
13729
13730   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13731   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13732
13733   RemovePlayer(player);
13734
13735   player->buried = TRUE;
13736
13737   if (game.all_players_gone)
13738     game.GameOver = TRUE;
13739 }
13740
13741 void RemovePlayer(struct PlayerInfo *player)
13742 {
13743   int jx = player->jx, jy = player->jy;
13744   int i, found = FALSE;
13745
13746   player->present = FALSE;
13747   player->active = FALSE;
13748
13749   // required for some CE actions (even if the player is not active anymore)
13750   player->MovPos = 0;
13751
13752   if (!ExplodeField[jx][jy])
13753     StorePlayer[jx][jy] = 0;
13754
13755   if (player->is_moving)
13756     TEST_DrawLevelField(player->last_jx, player->last_jy);
13757
13758   for (i = 0; i < MAX_PLAYERS; i++)
13759     if (stored_player[i].active)
13760       found = TRUE;
13761
13762   if (!found)
13763   {
13764     game.all_players_gone = TRUE;
13765     game.GameOver = TRUE;
13766   }
13767
13768   game.exit_x = game.robot_wheel_x = jx;
13769   game.exit_y = game.robot_wheel_y = jy;
13770 }
13771
13772 void ExitPlayer(struct PlayerInfo *player)
13773 {
13774   DrawPlayer(player);   // needed here only to cleanup last field
13775   RemovePlayer(player);
13776
13777   if (game.players_still_needed > 0)
13778     game.players_still_needed--;
13779 }
13780
13781 static void setFieldForSnapping(int x, int y, int element, int direction)
13782 {
13783   struct ElementInfo *ei = &element_info[element];
13784   int direction_bit = MV_DIR_TO_BIT(direction);
13785   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13786   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13787                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13788
13789   Tile[x][y] = EL_ELEMENT_SNAPPING;
13790   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13791
13792   ResetGfxAnimation(x, y);
13793
13794   GfxElement[x][y] = element;
13795   GfxAction[x][y] = action;
13796   GfxDir[x][y] = direction;
13797   GfxFrame[x][y] = -1;
13798 }
13799
13800 /*
13801   =============================================================================
13802   checkDiagonalPushing()
13803   -----------------------------------------------------------------------------
13804   check if diagonal input device direction results in pushing of object
13805   (by checking if the alternative direction is walkable, diggable, ...)
13806   =============================================================================
13807 */
13808
13809 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13810                                     int x, int y, int real_dx, int real_dy)
13811 {
13812   int jx, jy, dx, dy, xx, yy;
13813
13814   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13815     return TRUE;
13816
13817   // diagonal direction: check alternative direction
13818   jx = player->jx;
13819   jy = player->jy;
13820   dx = x - jx;
13821   dy = y - jy;
13822   xx = jx + (dx == 0 ? real_dx : 0);
13823   yy = jy + (dy == 0 ? real_dy : 0);
13824
13825   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
13826 }
13827
13828 /*
13829   =============================================================================
13830   DigField()
13831   -----------------------------------------------------------------------------
13832   x, y:                 field next to player (non-diagonal) to try to dig to
13833   real_dx, real_dy:     direction as read from input device (can be diagonal)
13834   =============================================================================
13835 */
13836
13837 static int DigField(struct PlayerInfo *player,
13838                     int oldx, int oldy, int x, int y,
13839                     int real_dx, int real_dy, int mode)
13840 {
13841   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13842   boolean player_was_pushing = player->is_pushing;
13843   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13844   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13845   int jx = oldx, jy = oldy;
13846   int dx = x - jx, dy = y - jy;
13847   int nextx = x + dx, nexty = y + dy;
13848   int move_direction = (dx == -1 ? MV_LEFT  :
13849                         dx == +1 ? MV_RIGHT :
13850                         dy == -1 ? MV_UP    :
13851                         dy == +1 ? MV_DOWN  : MV_NONE);
13852   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13853   int dig_side = MV_DIR_OPPOSITE(move_direction);
13854   int old_element = Tile[jx][jy];
13855   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13856   int collect_count;
13857
13858   if (is_player)                // function can also be called by EL_PENGUIN
13859   {
13860     if (player->MovPos == 0)
13861     {
13862       player->is_digging = FALSE;
13863       player->is_collecting = FALSE;
13864     }
13865
13866     if (player->MovPos == 0)    // last pushing move finished
13867       player->is_pushing = FALSE;
13868
13869     if (mode == DF_NO_PUSH)     // player just stopped pushing
13870     {
13871       player->is_switching = FALSE;
13872       player->push_delay = -1;
13873
13874       return MP_NO_ACTION;
13875     }
13876   }
13877
13878   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13879     old_element = Back[jx][jy];
13880
13881   // in case of element dropped at player position, check background
13882   else if (Back[jx][jy] != EL_EMPTY &&
13883            game.engine_version >= VERSION_IDENT(2,2,0,0))
13884     old_element = Back[jx][jy];
13885
13886   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13887     return MP_NO_ACTION;        // field has no opening in this direction
13888
13889   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13890     return MP_NO_ACTION;        // field has no opening in this direction
13891
13892   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13893   {
13894     SplashAcid(x, y);
13895
13896     Tile[jx][jy] = player->artwork_element;
13897     InitMovingField(jx, jy, MV_DOWN);
13898     Store[jx][jy] = EL_ACID;
13899     ContinueMoving(jx, jy);
13900     BuryPlayer(player);
13901
13902     return MP_DONT_RUN_INTO;
13903   }
13904
13905   if (player_can_move && DONT_RUN_INTO(element))
13906   {
13907     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13908
13909     return MP_DONT_RUN_INTO;
13910   }
13911
13912   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13913     return MP_NO_ACTION;
13914
13915   collect_count = element_info[element].collect_count_initial;
13916
13917   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
13918     return MP_NO_ACTION;
13919
13920   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13921     player_can_move = player_can_move_or_snap;
13922
13923   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13924       game.engine_version >= VERSION_IDENT(2,2,0,0))
13925   {
13926     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13927                                player->index_bit, dig_side);
13928     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13929                                         player->index_bit, dig_side);
13930
13931     if (element == EL_DC_LANDMINE)
13932       Bang(x, y);
13933
13934     if (Tile[x][y] != element)          // field changed by snapping
13935       return MP_ACTION;
13936
13937     return MP_NO_ACTION;
13938   }
13939
13940   if (player->gravity && is_player && !player->is_auto_moving &&
13941       canFallDown(player) && move_direction != MV_DOWN &&
13942       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13943     return MP_NO_ACTION;        // player cannot walk here due to gravity
13944
13945   if (player_can_move &&
13946       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13947   {
13948     int sound_element = SND_ELEMENT(element);
13949     int sound_action = ACTION_WALKING;
13950
13951     if (IS_RND_GATE(element))
13952     {
13953       if (!player->key[RND_GATE_NR(element)])
13954         return MP_NO_ACTION;
13955     }
13956     else if (IS_RND_GATE_GRAY(element))
13957     {
13958       if (!player->key[RND_GATE_GRAY_NR(element)])
13959         return MP_NO_ACTION;
13960     }
13961     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13962     {
13963       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13964         return MP_NO_ACTION;
13965     }
13966     else if (element == EL_EXIT_OPEN ||
13967              element == EL_EM_EXIT_OPEN ||
13968              element == EL_EM_EXIT_OPENING ||
13969              element == EL_STEEL_EXIT_OPEN ||
13970              element == EL_EM_STEEL_EXIT_OPEN ||
13971              element == EL_EM_STEEL_EXIT_OPENING ||
13972              element == EL_SP_EXIT_OPEN ||
13973              element == EL_SP_EXIT_OPENING)
13974     {
13975       sound_action = ACTION_PASSING;    // player is passing exit
13976     }
13977     else if (element == EL_EMPTY)
13978     {
13979       sound_action = ACTION_MOVING;             // nothing to walk on
13980     }
13981
13982     // play sound from background or player, whatever is available
13983     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13984       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13985     else
13986       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13987   }
13988   else if (player_can_move &&
13989            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13990   {
13991     if (!ACCESS_FROM(element, opposite_direction))
13992       return MP_NO_ACTION;      // field not accessible from this direction
13993
13994     if (CAN_MOVE(element))      // only fixed elements can be passed!
13995       return MP_NO_ACTION;
13996
13997     if (IS_EM_GATE(element))
13998     {
13999       if (!player->key[EM_GATE_NR(element)])
14000         return MP_NO_ACTION;
14001     }
14002     else if (IS_EM_GATE_GRAY(element))
14003     {
14004       if (!player->key[EM_GATE_GRAY_NR(element)])
14005         return MP_NO_ACTION;
14006     }
14007     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14008     {
14009       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14010         return MP_NO_ACTION;
14011     }
14012     else if (IS_EMC_GATE(element))
14013     {
14014       if (!player->key[EMC_GATE_NR(element)])
14015         return MP_NO_ACTION;
14016     }
14017     else if (IS_EMC_GATE_GRAY(element))
14018     {
14019       if (!player->key[EMC_GATE_GRAY_NR(element)])
14020         return MP_NO_ACTION;
14021     }
14022     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14023     {
14024       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14025         return MP_NO_ACTION;
14026     }
14027     else if (element == EL_DC_GATE_WHITE ||
14028              element == EL_DC_GATE_WHITE_GRAY ||
14029              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14030     {
14031       if (player->num_white_keys == 0)
14032         return MP_NO_ACTION;
14033
14034       player->num_white_keys--;
14035     }
14036     else if (IS_SP_PORT(element))
14037     {
14038       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14039           element == EL_SP_GRAVITY_PORT_RIGHT ||
14040           element == EL_SP_GRAVITY_PORT_UP ||
14041           element == EL_SP_GRAVITY_PORT_DOWN)
14042         player->gravity = !player->gravity;
14043       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14044                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14045                element == EL_SP_GRAVITY_ON_PORT_UP ||
14046                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14047         player->gravity = TRUE;
14048       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14049                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14050                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14051                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14052         player->gravity = FALSE;
14053     }
14054
14055     // automatically move to the next field with double speed
14056     player->programmed_action = move_direction;
14057
14058     if (player->move_delay_reset_counter == 0)
14059     {
14060       player->move_delay_reset_counter = 2;     // two double speed steps
14061
14062       DOUBLE_PLAYER_SPEED(player);
14063     }
14064
14065     PlayLevelSoundAction(x, y, ACTION_PASSING);
14066   }
14067   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14068   {
14069     RemoveField(x, y);
14070
14071     if (mode != DF_SNAP)
14072     {
14073       GfxElement[x][y] = GFX_ELEMENT(element);
14074       player->is_digging = TRUE;
14075     }
14076
14077     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14078
14079     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14080                                         player->index_bit, dig_side);
14081
14082     // if digging triggered player relocation, finish digging tile
14083     if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14084       setFieldForSnapping(x, y, element, move_direction);
14085
14086     if (mode == DF_SNAP)
14087     {
14088       if (level.block_snap_field)
14089         setFieldForSnapping(x, y, element, move_direction);
14090       else
14091         TestIfElementTouchesCustomElement(x, y);        // for empty space
14092
14093       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14094                                           player->index_bit, dig_side);
14095     }
14096   }
14097   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14098   {
14099     RemoveField(x, y);
14100
14101     if (is_player && mode != DF_SNAP)
14102     {
14103       GfxElement[x][y] = element;
14104       player->is_collecting = TRUE;
14105     }
14106
14107     if (element == EL_SPEED_PILL)
14108     {
14109       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14110     }
14111     else if (element == EL_EXTRA_TIME && level.time > 0)
14112     {
14113       TimeLeft += level.extra_time;
14114
14115       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14116
14117       DisplayGameControlValues();
14118     }
14119     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14120     {
14121       player->shield_normal_time_left += level.shield_normal_time;
14122       if (element == EL_SHIELD_DEADLY)
14123         player->shield_deadly_time_left += level.shield_deadly_time;
14124     }
14125     else if (element == EL_DYNAMITE ||
14126              element == EL_EM_DYNAMITE ||
14127              element == EL_SP_DISK_RED)
14128     {
14129       if (player->inventory_size < MAX_INVENTORY_SIZE)
14130         player->inventory_element[player->inventory_size++] = element;
14131
14132       DrawGameDoorValues();
14133     }
14134     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14135     {
14136       player->dynabomb_count++;
14137       player->dynabombs_left++;
14138     }
14139     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14140     {
14141       player->dynabomb_size++;
14142     }
14143     else if (element == EL_DYNABOMB_INCREASE_POWER)
14144     {
14145       player->dynabomb_xl = TRUE;
14146     }
14147     else if (IS_KEY(element))
14148     {
14149       player->key[KEY_NR(element)] = TRUE;
14150
14151       DrawGameDoorValues();
14152     }
14153     else if (element == EL_DC_KEY_WHITE)
14154     {
14155       player->num_white_keys++;
14156
14157       // display white keys?
14158       // DrawGameDoorValues();
14159     }
14160     else if (IS_ENVELOPE(element))
14161     {
14162       player->show_envelope = element;
14163     }
14164     else if (element == EL_EMC_LENSES)
14165     {
14166       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14167
14168       RedrawAllInvisibleElementsForLenses();
14169     }
14170     else if (element == EL_EMC_MAGNIFIER)
14171     {
14172       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14173
14174       RedrawAllInvisibleElementsForMagnifier();
14175     }
14176     else if (IS_DROPPABLE(element) ||
14177              IS_THROWABLE(element))     // can be collected and dropped
14178     {
14179       int i;
14180
14181       if (collect_count == 0)
14182         player->inventory_infinite_element = element;
14183       else
14184         for (i = 0; i < collect_count; i++)
14185           if (player->inventory_size < MAX_INVENTORY_SIZE)
14186             player->inventory_element[player->inventory_size++] = element;
14187
14188       DrawGameDoorValues();
14189     }
14190     else if (collect_count > 0)
14191     {
14192       game.gems_still_needed -= collect_count;
14193       if (game.gems_still_needed < 0)
14194         game.gems_still_needed = 0;
14195
14196       game.snapshot.collected_item = TRUE;
14197
14198       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14199
14200       DisplayGameControlValues();
14201     }
14202
14203     RaiseScoreElement(element);
14204     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14205
14206     if (is_player)
14207     {
14208       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14209                                           player->index_bit, dig_side);
14210
14211       // if collecting triggered player relocation, finish collecting tile
14212       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14213         setFieldForSnapping(x, y, element, move_direction);
14214     }
14215
14216     if (mode == DF_SNAP)
14217     {
14218       if (level.block_snap_field)
14219         setFieldForSnapping(x, y, element, move_direction);
14220       else
14221         TestIfElementTouchesCustomElement(x, y);        // for empty space
14222
14223       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14224                                           player->index_bit, dig_side);
14225     }
14226   }
14227   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14228   {
14229     if (mode == DF_SNAP && element != EL_BD_ROCK)
14230       return MP_NO_ACTION;
14231
14232     if (CAN_FALL(element) && dy)
14233       return MP_NO_ACTION;
14234
14235     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14236         !(element == EL_SPRING && level.use_spring_bug))
14237       return MP_NO_ACTION;
14238
14239     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14240         ((move_direction & MV_VERTICAL &&
14241           ((element_info[element].move_pattern & MV_LEFT &&
14242             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14243            (element_info[element].move_pattern & MV_RIGHT &&
14244             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14245          (move_direction & MV_HORIZONTAL &&
14246           ((element_info[element].move_pattern & MV_UP &&
14247             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14248            (element_info[element].move_pattern & MV_DOWN &&
14249             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14250       return MP_NO_ACTION;
14251
14252     // do not push elements already moving away faster than player
14253     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14254         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14255       return MP_NO_ACTION;
14256
14257     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14258     {
14259       if (player->push_delay_value == -1 || !player_was_pushing)
14260         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14261     }
14262     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14263     {
14264       if (player->push_delay_value == -1)
14265         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14266     }
14267     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14268     {
14269       if (!player->is_pushing)
14270         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14271     }
14272
14273     player->is_pushing = TRUE;
14274     player->is_active = TRUE;
14275
14276     if (!(IN_LEV_FIELD(nextx, nexty) &&
14277           (IS_FREE(nextx, nexty) ||
14278            (IS_SB_ELEMENT(element) &&
14279             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14280            (IS_CUSTOM_ELEMENT(element) &&
14281             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14282       return MP_NO_ACTION;
14283
14284     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14285       return MP_NO_ACTION;
14286
14287     if (player->push_delay == -1)       // new pushing; restart delay
14288       player->push_delay = 0;
14289
14290     if (player->push_delay < player->push_delay_value &&
14291         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14292         element != EL_SPRING && element != EL_BALLOON)
14293     {
14294       // make sure that there is no move delay before next try to push
14295       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14296         player->move_delay = 0;
14297
14298       return MP_NO_ACTION;
14299     }
14300
14301     if (IS_CUSTOM_ELEMENT(element) &&
14302         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14303     {
14304       if (!DigFieldByCE(nextx, nexty, element))
14305         return MP_NO_ACTION;
14306     }
14307
14308     if (IS_SB_ELEMENT(element))
14309     {
14310       boolean sokoban_task_solved = FALSE;
14311
14312       if (element == EL_SOKOBAN_FIELD_FULL)
14313       {
14314         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14315
14316         IncrementSokobanFieldsNeeded();
14317         IncrementSokobanObjectsNeeded();
14318       }
14319
14320       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14321       {
14322         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14323
14324         DecrementSokobanFieldsNeeded();
14325         DecrementSokobanObjectsNeeded();
14326
14327         // sokoban object was pushed from empty field to sokoban field
14328         if (Back[x][y] == EL_EMPTY)
14329           sokoban_task_solved = TRUE;
14330       }
14331
14332       Tile[x][y] = EL_SOKOBAN_OBJECT;
14333
14334       if (Back[x][y] == Back[nextx][nexty])
14335         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14336       else if (Back[x][y] != 0)
14337         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14338                                     ACTION_EMPTYING);
14339       else
14340         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14341                                     ACTION_FILLING);
14342
14343       if (sokoban_task_solved &&
14344           game.sokoban_fields_still_needed == 0 &&
14345           game.sokoban_objects_still_needed == 0 &&
14346           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14347       {
14348         game.players_still_needed = 0;
14349
14350         LevelSolved();
14351
14352         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14353       }
14354     }
14355     else
14356       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14357
14358     InitMovingField(x, y, move_direction);
14359     GfxAction[x][y] = ACTION_PUSHING;
14360
14361     if (mode == DF_SNAP)
14362       ContinueMoving(x, y);
14363     else
14364       MovPos[x][y] = (dx != 0 ? dx : dy);
14365
14366     Pushed[x][y] = TRUE;
14367     Pushed[nextx][nexty] = TRUE;
14368
14369     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14370       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14371     else
14372       player->push_delay_value = -1;    // get new value later
14373
14374     // check for element change _after_ element has been pushed
14375     if (game.use_change_when_pushing_bug)
14376     {
14377       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14378                                  player->index_bit, dig_side);
14379       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14380                                           player->index_bit, dig_side);
14381     }
14382   }
14383   else if (IS_SWITCHABLE(element))
14384   {
14385     if (PLAYER_SWITCHING(player, x, y))
14386     {
14387       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14388                                           player->index_bit, dig_side);
14389
14390       return MP_ACTION;
14391     }
14392
14393     player->is_switching = TRUE;
14394     player->switch_x = x;
14395     player->switch_y = y;
14396
14397     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14398
14399     if (element == EL_ROBOT_WHEEL)
14400     {
14401       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14402
14403       game.robot_wheel_x = x;
14404       game.robot_wheel_y = y;
14405       game.robot_wheel_active = TRUE;
14406
14407       TEST_DrawLevelField(x, y);
14408     }
14409     else if (element == EL_SP_TERMINAL)
14410     {
14411       int xx, yy;
14412
14413       SCAN_PLAYFIELD(xx, yy)
14414       {
14415         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14416         {
14417           Bang(xx, yy);
14418         }
14419         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14420         {
14421           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14422
14423           ResetGfxAnimation(xx, yy);
14424           TEST_DrawLevelField(xx, yy);
14425         }
14426       }
14427     }
14428     else if (IS_BELT_SWITCH(element))
14429     {
14430       ToggleBeltSwitch(x, y);
14431     }
14432     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14433              element == EL_SWITCHGATE_SWITCH_DOWN ||
14434              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14435              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14436     {
14437       ToggleSwitchgateSwitch(x, y);
14438     }
14439     else if (element == EL_LIGHT_SWITCH ||
14440              element == EL_LIGHT_SWITCH_ACTIVE)
14441     {
14442       ToggleLightSwitch(x, y);
14443     }
14444     else if (element == EL_TIMEGATE_SWITCH ||
14445              element == EL_DC_TIMEGATE_SWITCH)
14446     {
14447       ActivateTimegateSwitch(x, y);
14448     }
14449     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14450              element == EL_BALLOON_SWITCH_RIGHT ||
14451              element == EL_BALLOON_SWITCH_UP    ||
14452              element == EL_BALLOON_SWITCH_DOWN  ||
14453              element == EL_BALLOON_SWITCH_NONE  ||
14454              element == EL_BALLOON_SWITCH_ANY)
14455     {
14456       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14457                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14458                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14459                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14460                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14461                              move_direction);
14462     }
14463     else if (element == EL_LAMP)
14464     {
14465       Tile[x][y] = EL_LAMP_ACTIVE;
14466       game.lights_still_needed--;
14467
14468       ResetGfxAnimation(x, y);
14469       TEST_DrawLevelField(x, y);
14470     }
14471     else if (element == EL_TIME_ORB_FULL)
14472     {
14473       Tile[x][y] = EL_TIME_ORB_EMPTY;
14474
14475       if (level.time > 0 || level.use_time_orb_bug)
14476       {
14477         TimeLeft += level.time_orb_time;
14478         game.no_time_limit = FALSE;
14479
14480         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14481
14482         DisplayGameControlValues();
14483       }
14484
14485       ResetGfxAnimation(x, y);
14486       TEST_DrawLevelField(x, y);
14487     }
14488     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14489              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14490     {
14491       int xx, yy;
14492
14493       game.ball_active = !game.ball_active;
14494
14495       SCAN_PLAYFIELD(xx, yy)
14496       {
14497         int e = Tile[xx][yy];
14498
14499         if (game.ball_active)
14500         {
14501           if (e == EL_EMC_MAGIC_BALL)
14502             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14503           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14504             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14505         }
14506         else
14507         {
14508           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14509             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14510           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14511             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14512         }
14513       }
14514     }
14515
14516     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14517                                         player->index_bit, dig_side);
14518
14519     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14520                                         player->index_bit, dig_side);
14521
14522     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14523                                         player->index_bit, dig_side);
14524
14525     return MP_ACTION;
14526   }
14527   else
14528   {
14529     if (!PLAYER_SWITCHING(player, x, y))
14530     {
14531       player->is_switching = TRUE;
14532       player->switch_x = x;
14533       player->switch_y = y;
14534
14535       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14536                                  player->index_bit, dig_side);
14537       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14538                                           player->index_bit, dig_side);
14539
14540       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14541                                  player->index_bit, dig_side);
14542       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14543                                           player->index_bit, dig_side);
14544     }
14545
14546     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14547                                player->index_bit, dig_side);
14548     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14549                                         player->index_bit, dig_side);
14550
14551     return MP_NO_ACTION;
14552   }
14553
14554   player->push_delay = -1;
14555
14556   if (is_player)                // function can also be called by EL_PENGUIN
14557   {
14558     if (Tile[x][y] != element)          // really digged/collected something
14559     {
14560       player->is_collecting = !player->is_digging;
14561       player->is_active = TRUE;
14562     }
14563   }
14564
14565   return MP_MOVING;
14566 }
14567
14568 static boolean DigFieldByCE(int x, int y, int digging_element)
14569 {
14570   int element = Tile[x][y];
14571
14572   if (!IS_FREE(x, y))
14573   {
14574     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14575                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14576                   ACTION_BREAKING);
14577
14578     // no element can dig solid indestructible elements
14579     if (IS_INDESTRUCTIBLE(element) &&
14580         !IS_DIGGABLE(element) &&
14581         !IS_COLLECTIBLE(element))
14582       return FALSE;
14583
14584     if (AmoebaNr[x][y] &&
14585         (element == EL_AMOEBA_FULL ||
14586          element == EL_BD_AMOEBA ||
14587          element == EL_AMOEBA_GROWING))
14588     {
14589       AmoebaCnt[AmoebaNr[x][y]]--;
14590       AmoebaCnt2[AmoebaNr[x][y]]--;
14591     }
14592
14593     if (IS_MOVING(x, y))
14594       RemoveMovingField(x, y);
14595     else
14596     {
14597       RemoveField(x, y);
14598       TEST_DrawLevelField(x, y);
14599     }
14600
14601     // if digged element was about to explode, prevent the explosion
14602     ExplodeField[x][y] = EX_TYPE_NONE;
14603
14604     PlayLevelSoundAction(x, y, action);
14605   }
14606
14607   Store[x][y] = EL_EMPTY;
14608
14609   // this makes it possible to leave the removed element again
14610   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14611     Store[x][y] = element;
14612
14613   return TRUE;
14614 }
14615
14616 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14617 {
14618   int jx = player->jx, jy = player->jy;
14619   int x = jx + dx, y = jy + dy;
14620   int snap_direction = (dx == -1 ? MV_LEFT  :
14621                         dx == +1 ? MV_RIGHT :
14622                         dy == -1 ? MV_UP    :
14623                         dy == +1 ? MV_DOWN  : MV_NONE);
14624   boolean can_continue_snapping = (level.continuous_snapping &&
14625                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14626
14627   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14628     return FALSE;
14629
14630   if (!player->active || !IN_LEV_FIELD(x, y))
14631     return FALSE;
14632
14633   if (dx && dy)
14634     return FALSE;
14635
14636   if (!dx && !dy)
14637   {
14638     if (player->MovPos == 0)
14639       player->is_pushing = FALSE;
14640
14641     player->is_snapping = FALSE;
14642
14643     if (player->MovPos == 0)
14644     {
14645       player->is_moving = FALSE;
14646       player->is_digging = FALSE;
14647       player->is_collecting = FALSE;
14648     }
14649
14650     return FALSE;
14651   }
14652
14653   // prevent snapping with already pressed snap key when not allowed
14654   if (player->is_snapping && !can_continue_snapping)
14655     return FALSE;
14656
14657   player->MovDir = snap_direction;
14658
14659   if (player->MovPos == 0)
14660   {
14661     player->is_moving = FALSE;
14662     player->is_digging = FALSE;
14663     player->is_collecting = FALSE;
14664   }
14665
14666   player->is_dropping = FALSE;
14667   player->is_dropping_pressed = FALSE;
14668   player->drop_pressed_delay = 0;
14669
14670   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14671     return FALSE;
14672
14673   player->is_snapping = TRUE;
14674   player->is_active = TRUE;
14675
14676   if (player->MovPos == 0)
14677   {
14678     player->is_moving = FALSE;
14679     player->is_digging = FALSE;
14680     player->is_collecting = FALSE;
14681   }
14682
14683   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14684     TEST_DrawLevelField(player->last_jx, player->last_jy);
14685
14686   TEST_DrawLevelField(x, y);
14687
14688   return TRUE;
14689 }
14690
14691 static boolean DropElement(struct PlayerInfo *player)
14692 {
14693   int old_element, new_element;
14694   int dropx = player->jx, dropy = player->jy;
14695   int drop_direction = player->MovDir;
14696   int drop_side = drop_direction;
14697   int drop_element = get_next_dropped_element(player);
14698
14699   /* do not drop an element on top of another element; when holding drop key
14700      pressed without moving, dropped element must move away before the next
14701      element can be dropped (this is especially important if the next element
14702      is dynamite, which can be placed on background for historical reasons) */
14703   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
14704     return MP_ACTION;
14705
14706   if (IS_THROWABLE(drop_element))
14707   {
14708     dropx += GET_DX_FROM_DIR(drop_direction);
14709     dropy += GET_DY_FROM_DIR(drop_direction);
14710
14711     if (!IN_LEV_FIELD(dropx, dropy))
14712       return FALSE;
14713   }
14714
14715   old_element = Tile[dropx][dropy];     // old element at dropping position
14716   new_element = drop_element;           // default: no change when dropping
14717
14718   // check if player is active, not moving and ready to drop
14719   if (!player->active || player->MovPos || player->drop_delay > 0)
14720     return FALSE;
14721
14722   // check if player has anything that can be dropped
14723   if (new_element == EL_UNDEFINED)
14724     return FALSE;
14725
14726   // only set if player has anything that can be dropped
14727   player->is_dropping_pressed = TRUE;
14728
14729   // check if drop key was pressed long enough for EM style dynamite
14730   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14731     return FALSE;
14732
14733   // check if anything can be dropped at the current position
14734   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14735     return FALSE;
14736
14737   // collected custom elements can only be dropped on empty fields
14738   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14739     return FALSE;
14740
14741   if (old_element != EL_EMPTY)
14742     Back[dropx][dropy] = old_element;   // store old element on this field
14743
14744   ResetGfxAnimation(dropx, dropy);
14745   ResetRandomAnimationValue(dropx, dropy);
14746
14747   if (player->inventory_size > 0 ||
14748       player->inventory_infinite_element != EL_UNDEFINED)
14749   {
14750     if (player->inventory_size > 0)
14751     {
14752       player->inventory_size--;
14753
14754       DrawGameDoorValues();
14755
14756       if (new_element == EL_DYNAMITE)
14757         new_element = EL_DYNAMITE_ACTIVE;
14758       else if (new_element == EL_EM_DYNAMITE)
14759         new_element = EL_EM_DYNAMITE_ACTIVE;
14760       else if (new_element == EL_SP_DISK_RED)
14761         new_element = EL_SP_DISK_RED_ACTIVE;
14762     }
14763
14764     Tile[dropx][dropy] = new_element;
14765
14766     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14767       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14768                           el2img(Tile[dropx][dropy]), 0);
14769
14770     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14771
14772     // needed if previous element just changed to "empty" in the last frame
14773     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14774
14775     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14776                                player->index_bit, drop_side);
14777     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14778                                         CE_PLAYER_DROPS_X,
14779                                         player->index_bit, drop_side);
14780
14781     TestIfElementTouchesCustomElement(dropx, dropy);
14782   }
14783   else          // player is dropping a dyna bomb
14784   {
14785     player->dynabombs_left--;
14786
14787     Tile[dropx][dropy] = new_element;
14788
14789     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14790       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14791                           el2img(Tile[dropx][dropy]), 0);
14792
14793     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14794   }
14795
14796   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
14797     InitField_WithBug1(dropx, dropy, FALSE);
14798
14799   new_element = Tile[dropx][dropy];     // element might have changed
14800
14801   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14802       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14803   {
14804     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14805       MovDir[dropx][dropy] = drop_direction;
14806
14807     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14808
14809     // do not cause impact style collision by dropping elements that can fall
14810     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14811   }
14812
14813   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14814   player->is_dropping = TRUE;
14815
14816   player->drop_pressed_delay = 0;
14817   player->is_dropping_pressed = FALSE;
14818
14819   player->drop_x = dropx;
14820   player->drop_y = dropy;
14821
14822   return TRUE;
14823 }
14824
14825 // ----------------------------------------------------------------------------
14826 // game sound playing functions
14827 // ----------------------------------------------------------------------------
14828
14829 static int *loop_sound_frame = NULL;
14830 static int *loop_sound_volume = NULL;
14831
14832 void InitPlayLevelSound(void)
14833 {
14834   int num_sounds = getSoundListSize();
14835
14836   checked_free(loop_sound_frame);
14837   checked_free(loop_sound_volume);
14838
14839   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14840   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14841 }
14842
14843 static void PlayLevelSound(int x, int y, int nr)
14844 {
14845   int sx = SCREENX(x), sy = SCREENY(y);
14846   int volume, stereo_position;
14847   int max_distance = 8;
14848   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14849
14850   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14851       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14852     return;
14853
14854   if (!IN_LEV_FIELD(x, y) ||
14855       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14856       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14857     return;
14858
14859   volume = SOUND_MAX_VOLUME;
14860
14861   if (!IN_SCR_FIELD(sx, sy))
14862   {
14863     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14864     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14865
14866     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14867   }
14868
14869   stereo_position = (SOUND_MAX_LEFT +
14870                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14871                      (SCR_FIELDX + 2 * max_distance));
14872
14873   if (IS_LOOP_SOUND(nr))
14874   {
14875     /* This assures that quieter loop sounds do not overwrite louder ones,
14876        while restarting sound volume comparison with each new game frame. */
14877
14878     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14879       return;
14880
14881     loop_sound_volume[nr] = volume;
14882     loop_sound_frame[nr] = FrameCounter;
14883   }
14884
14885   PlaySoundExt(nr, volume, stereo_position, type);
14886 }
14887
14888 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14889 {
14890   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14891                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14892                  y < LEVELY(BY1) ? LEVELY(BY1) :
14893                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14894                  sound_action);
14895 }
14896
14897 static void PlayLevelSoundAction(int x, int y, int action)
14898 {
14899   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
14900 }
14901
14902 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14903 {
14904   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14905
14906   if (sound_effect != SND_UNDEFINED)
14907     PlayLevelSound(x, y, sound_effect);
14908 }
14909
14910 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14911                                               int action)
14912 {
14913   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14914
14915   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14916     PlayLevelSound(x, y, sound_effect);
14917 }
14918
14919 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14920 {
14921   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
14922
14923   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14924     PlayLevelSound(x, y, sound_effect);
14925 }
14926
14927 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14928 {
14929   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
14930
14931   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14932     StopSound(sound_effect);
14933 }
14934
14935 static int getLevelMusicNr(void)
14936 {
14937   if (levelset.music[level_nr] != MUS_UNDEFINED)
14938     return levelset.music[level_nr];            // from config file
14939   else
14940     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
14941 }
14942
14943 static void FadeLevelSounds(void)
14944 {
14945   FadeSounds();
14946 }
14947
14948 static void FadeLevelMusic(void)
14949 {
14950   int music_nr = getLevelMusicNr();
14951   char *curr_music = getCurrentlyPlayingMusicFilename();
14952   char *next_music = getMusicInfoEntryFilename(music_nr);
14953
14954   if (!strEqual(curr_music, next_music))
14955     FadeMusic();
14956 }
14957
14958 void FadeLevelSoundsAndMusic(void)
14959 {
14960   FadeLevelSounds();
14961   FadeLevelMusic();
14962 }
14963
14964 static void PlayLevelMusic(void)
14965 {
14966   int music_nr = getLevelMusicNr();
14967   char *curr_music = getCurrentlyPlayingMusicFilename();
14968   char *next_music = getMusicInfoEntryFilename(music_nr);
14969
14970   if (!strEqual(curr_music, next_music))
14971     PlayMusicLoop(music_nr);
14972 }
14973
14974 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14975 {
14976   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
14977   int offset = 0;
14978   int x = xx - offset;
14979   int y = yy - offset;
14980
14981   switch (sample)
14982   {
14983     case SOUND_blank:
14984       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14985       break;
14986
14987     case SOUND_roll:
14988       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14989       break;
14990
14991     case SOUND_stone:
14992       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14993       break;
14994
14995     case SOUND_nut:
14996       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14997       break;
14998
14999     case SOUND_crack:
15000       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15001       break;
15002
15003     case SOUND_bug:
15004       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15005       break;
15006
15007     case SOUND_tank:
15008       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15009       break;
15010
15011     case SOUND_android_clone:
15012       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15013       break;
15014
15015     case SOUND_android_move:
15016       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15017       break;
15018
15019     case SOUND_spring:
15020       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15021       break;
15022
15023     case SOUND_slurp:
15024       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15025       break;
15026
15027     case SOUND_eater:
15028       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15029       break;
15030
15031     case SOUND_eater_eat:
15032       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15033       break;
15034
15035     case SOUND_alien:
15036       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15037       break;
15038
15039     case SOUND_collect:
15040       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15041       break;
15042
15043     case SOUND_diamond:
15044       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15045       break;
15046
15047     case SOUND_squash:
15048       // !!! CHECK THIS !!!
15049 #if 1
15050       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15051 #else
15052       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15053 #endif
15054       break;
15055
15056     case SOUND_wonderfall:
15057       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15058       break;
15059
15060     case SOUND_drip:
15061       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15062       break;
15063
15064     case SOUND_push:
15065       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15066       break;
15067
15068     case SOUND_dirt:
15069       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15070       break;
15071
15072     case SOUND_acid:
15073       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15074       break;
15075
15076     case SOUND_ball:
15077       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15078       break;
15079
15080     case SOUND_slide:
15081       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15082       break;
15083
15084     case SOUND_wonder:
15085       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15086       break;
15087
15088     case SOUND_door:
15089       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15090       break;
15091
15092     case SOUND_exit_open:
15093       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15094       break;
15095
15096     case SOUND_exit_leave:
15097       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15098       break;
15099
15100     case SOUND_dynamite:
15101       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15102       break;
15103
15104     case SOUND_tick:
15105       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15106       break;
15107
15108     case SOUND_press:
15109       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15110       break;
15111
15112     case SOUND_wheel:
15113       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15114       break;
15115
15116     case SOUND_boom:
15117       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15118       break;
15119
15120     case SOUND_die:
15121       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15122       break;
15123
15124     case SOUND_time:
15125       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15126       break;
15127
15128     default:
15129       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15130       break;
15131   }
15132 }
15133
15134 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15135 {
15136   int element = map_element_SP_to_RND(element_sp);
15137   int action = map_action_SP_to_RND(action_sp);
15138   int offset = (setup.sp_show_border_elements ? 0 : 1);
15139   int x = xx - offset;
15140   int y = yy - offset;
15141
15142   PlayLevelSoundElementAction(x, y, element, action);
15143 }
15144
15145 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15146 {
15147   int element = map_element_MM_to_RND(element_mm);
15148   int action = map_action_MM_to_RND(action_mm);
15149   int offset = 0;
15150   int x = xx - offset;
15151   int y = yy - offset;
15152
15153   if (!IS_MM_ELEMENT(element))
15154     element = EL_MM_DEFAULT;
15155
15156   PlayLevelSoundElementAction(x, y, element, action);
15157 }
15158
15159 void PlaySound_MM(int sound_mm)
15160 {
15161   int sound = map_sound_MM_to_RND(sound_mm);
15162
15163   if (sound == SND_UNDEFINED)
15164     return;
15165
15166   PlaySound(sound);
15167 }
15168
15169 void PlaySoundLoop_MM(int sound_mm)
15170 {
15171   int sound = map_sound_MM_to_RND(sound_mm);
15172
15173   if (sound == SND_UNDEFINED)
15174     return;
15175
15176   PlaySoundLoop(sound);
15177 }
15178
15179 void StopSound_MM(int sound_mm)
15180 {
15181   int sound = map_sound_MM_to_RND(sound_mm);
15182
15183   if (sound == SND_UNDEFINED)
15184     return;
15185
15186   StopSound(sound);
15187 }
15188
15189 void RaiseScore(int value)
15190 {
15191   game.score += value;
15192
15193   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15194
15195   DisplayGameControlValues();
15196 }
15197
15198 void RaiseScoreElement(int element)
15199 {
15200   switch (element)
15201   {
15202     case EL_EMERALD:
15203     case EL_BD_DIAMOND:
15204     case EL_EMERALD_YELLOW:
15205     case EL_EMERALD_RED:
15206     case EL_EMERALD_PURPLE:
15207     case EL_SP_INFOTRON:
15208       RaiseScore(level.score[SC_EMERALD]);
15209       break;
15210     case EL_DIAMOND:
15211       RaiseScore(level.score[SC_DIAMOND]);
15212       break;
15213     case EL_CRYSTAL:
15214       RaiseScore(level.score[SC_CRYSTAL]);
15215       break;
15216     case EL_PEARL:
15217       RaiseScore(level.score[SC_PEARL]);
15218       break;
15219     case EL_BUG:
15220     case EL_BD_BUTTERFLY:
15221     case EL_SP_ELECTRON:
15222       RaiseScore(level.score[SC_BUG]);
15223       break;
15224     case EL_SPACESHIP:
15225     case EL_BD_FIREFLY:
15226     case EL_SP_SNIKSNAK:
15227       RaiseScore(level.score[SC_SPACESHIP]);
15228       break;
15229     case EL_YAMYAM:
15230     case EL_DARK_YAMYAM:
15231       RaiseScore(level.score[SC_YAMYAM]);
15232       break;
15233     case EL_ROBOT:
15234       RaiseScore(level.score[SC_ROBOT]);
15235       break;
15236     case EL_PACMAN:
15237       RaiseScore(level.score[SC_PACMAN]);
15238       break;
15239     case EL_NUT:
15240       RaiseScore(level.score[SC_NUT]);
15241       break;
15242     case EL_DYNAMITE:
15243     case EL_EM_DYNAMITE:
15244     case EL_SP_DISK_RED:
15245     case EL_DYNABOMB_INCREASE_NUMBER:
15246     case EL_DYNABOMB_INCREASE_SIZE:
15247     case EL_DYNABOMB_INCREASE_POWER:
15248       RaiseScore(level.score[SC_DYNAMITE]);
15249       break;
15250     case EL_SHIELD_NORMAL:
15251     case EL_SHIELD_DEADLY:
15252       RaiseScore(level.score[SC_SHIELD]);
15253       break;
15254     case EL_EXTRA_TIME:
15255       RaiseScore(level.extra_time_score);
15256       break;
15257     case EL_KEY_1:
15258     case EL_KEY_2:
15259     case EL_KEY_3:
15260     case EL_KEY_4:
15261     case EL_EM_KEY_1:
15262     case EL_EM_KEY_2:
15263     case EL_EM_KEY_3:
15264     case EL_EM_KEY_4:
15265     case EL_EMC_KEY_5:
15266     case EL_EMC_KEY_6:
15267     case EL_EMC_KEY_7:
15268     case EL_EMC_KEY_8:
15269     case EL_DC_KEY_WHITE:
15270       RaiseScore(level.score[SC_KEY]);
15271       break;
15272     default:
15273       RaiseScore(element_info[element].collect_score);
15274       break;
15275   }
15276 }
15277
15278 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15279 {
15280   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15281   {
15282     // closing door required in case of envelope style request dialogs
15283     if (!skip_request)
15284     {
15285       // prevent short reactivation of overlay buttons while closing door
15286       SetOverlayActive(FALSE);
15287
15288       CloseDoor(DOOR_CLOSE_1);
15289     }
15290
15291     if (network.enabled)
15292       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15293     else
15294     {
15295       if (quick_quit)
15296         FadeSkipNextFadeIn();
15297
15298       SetGameStatus(GAME_MODE_MAIN);
15299
15300       DrawMainMenu();
15301     }
15302   }
15303   else          // continue playing the game
15304   {
15305     if (tape.playing && tape.deactivate_display)
15306       TapeDeactivateDisplayOff(TRUE);
15307
15308     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15309
15310     if (tape.playing && tape.deactivate_display)
15311       TapeDeactivateDisplayOn();
15312   }
15313 }
15314
15315 void RequestQuitGame(boolean ask_if_really_quit)
15316 {
15317   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15318   boolean skip_request = game.all_players_gone || quick_quit;
15319
15320   RequestQuitGameExt(skip_request, quick_quit,
15321                      "Do you really want to quit the game?");
15322 }
15323
15324 void RequestRestartGame(char *message)
15325 {
15326   game.restart_game_message = NULL;
15327
15328   boolean has_started_game = hasStartedNetworkGame();
15329   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15330
15331   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15332   {
15333     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15334   }
15335   else
15336   {
15337     // needed in case of envelope request to close game panel
15338     CloseDoor(DOOR_CLOSE_1);
15339
15340     SetGameStatus(GAME_MODE_MAIN);
15341
15342     DrawMainMenu();
15343   }
15344 }
15345
15346 void CheckGameOver(void)
15347 {
15348   static boolean last_game_over = FALSE;
15349   static int game_over_delay = 0;
15350   int game_over_delay_value = 50;
15351   boolean game_over = checkGameFailed();
15352
15353   // do not handle game over if request dialog is already active
15354   if (game.request_active)
15355     return;
15356
15357   // do not ask to play again if game was never actually played
15358   if (!game.GamePlayed)
15359     return;
15360
15361   if (!game_over)
15362   {
15363     last_game_over = FALSE;
15364     game_over_delay = game_over_delay_value;
15365
15366     return;
15367   }
15368
15369   if (game_over_delay > 0)
15370   {
15371     game_over_delay--;
15372
15373     return;
15374   }
15375
15376   if (last_game_over != game_over)
15377     game.restart_game_message = (hasStartedNetworkGame() ?
15378                                  "Game over! Play it again?" :
15379                                  "Game over!");
15380
15381   last_game_over = game_over;
15382 }
15383
15384 boolean checkGameSolved(void)
15385 {
15386   // set for all game engines if level was solved
15387   return game.LevelSolved_GameEnd;
15388 }
15389
15390 boolean checkGameFailed(void)
15391 {
15392   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15393     return (game_em.game_over && !game_em.level_solved);
15394   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15395     return (game_sp.game_over && !game_sp.level_solved);
15396   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15397     return (game_mm.game_over && !game_mm.level_solved);
15398   else                          // GAME_ENGINE_TYPE_RND
15399     return (game.GameOver && !game.LevelSolved);
15400 }
15401
15402 boolean checkGameEnded(void)
15403 {
15404   return (checkGameSolved() || checkGameFailed());
15405 }
15406
15407
15408 // ----------------------------------------------------------------------------
15409 // random generator functions
15410 // ----------------------------------------------------------------------------
15411
15412 unsigned int InitEngineRandom_RND(int seed)
15413 {
15414   game.num_random_calls = 0;
15415
15416   return InitEngineRandom(seed);
15417 }
15418
15419 unsigned int RND(int max)
15420 {
15421   if (max > 0)
15422   {
15423     game.num_random_calls++;
15424
15425     return GetEngineRandom(max);
15426   }
15427
15428   return 0;
15429 }
15430
15431
15432 // ----------------------------------------------------------------------------
15433 // game engine snapshot handling functions
15434 // ----------------------------------------------------------------------------
15435
15436 struct EngineSnapshotInfo
15437 {
15438   // runtime values for custom element collect score
15439   int collect_score[NUM_CUSTOM_ELEMENTS];
15440
15441   // runtime values for group element choice position
15442   int choice_pos[NUM_GROUP_ELEMENTS];
15443
15444   // runtime values for belt position animations
15445   int belt_graphic[4][NUM_BELT_PARTS];
15446   int belt_anim_mode[4][NUM_BELT_PARTS];
15447 };
15448
15449 static struct EngineSnapshotInfo engine_snapshot_rnd;
15450 static char *snapshot_level_identifier = NULL;
15451 static int snapshot_level_nr = -1;
15452
15453 static void SaveEngineSnapshotValues_RND(void)
15454 {
15455   static int belt_base_active_element[4] =
15456   {
15457     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15458     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15459     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15460     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15461   };
15462   int i, j;
15463
15464   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15465   {
15466     int element = EL_CUSTOM_START + i;
15467
15468     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15469   }
15470
15471   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15472   {
15473     int element = EL_GROUP_START + i;
15474
15475     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15476   }
15477
15478   for (i = 0; i < 4; i++)
15479   {
15480     for (j = 0; j < NUM_BELT_PARTS; j++)
15481     {
15482       int element = belt_base_active_element[i] + j;
15483       int graphic = el2img(element);
15484       int anim_mode = graphic_info[graphic].anim_mode;
15485
15486       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15487       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15488     }
15489   }
15490 }
15491
15492 static void LoadEngineSnapshotValues_RND(void)
15493 {
15494   unsigned int num_random_calls = game.num_random_calls;
15495   int i, j;
15496
15497   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15498   {
15499     int element = EL_CUSTOM_START + i;
15500
15501     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15502   }
15503
15504   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15505   {
15506     int element = EL_GROUP_START + i;
15507
15508     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15509   }
15510
15511   for (i = 0; i < 4; i++)
15512   {
15513     for (j = 0; j < NUM_BELT_PARTS; j++)
15514     {
15515       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15516       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15517
15518       graphic_info[graphic].anim_mode = anim_mode;
15519     }
15520   }
15521
15522   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15523   {
15524     InitRND(tape.random_seed);
15525     for (i = 0; i < num_random_calls; i++)
15526       RND(1);
15527   }
15528
15529   if (game.num_random_calls != num_random_calls)
15530   {
15531     Error("number of random calls out of sync");
15532     Error("number of random calls should be %d", num_random_calls);
15533     Error("number of random calls is %d", game.num_random_calls);
15534
15535     Fail("this should not happen -- please debug");
15536   }
15537 }
15538
15539 void FreeEngineSnapshotSingle(void)
15540 {
15541   FreeSnapshotSingle();
15542
15543   setString(&snapshot_level_identifier, NULL);
15544   snapshot_level_nr = -1;
15545 }
15546
15547 void FreeEngineSnapshotList(void)
15548 {
15549   FreeSnapshotList();
15550 }
15551
15552 static ListNode *SaveEngineSnapshotBuffers(void)
15553 {
15554   ListNode *buffers = NULL;
15555
15556   // copy some special values to a structure better suited for the snapshot
15557
15558   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15559     SaveEngineSnapshotValues_RND();
15560   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15561     SaveEngineSnapshotValues_EM();
15562   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15563     SaveEngineSnapshotValues_SP(&buffers);
15564   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15565     SaveEngineSnapshotValues_MM(&buffers);
15566
15567   // save values stored in special snapshot structure
15568
15569   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15570     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15571   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15572     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15573   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15574     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15575   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15576     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15577
15578   // save further RND engine values
15579
15580   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15581   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15582   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15583
15584   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15585   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15586   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15587   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15588   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15589
15590   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15591   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15592   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15593
15594   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15595
15596   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15597   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15598
15599   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15600   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15601   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15602   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15603   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15604   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15605   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15606   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15607   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15608   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15609   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15610   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15611   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15612   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15613   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15614   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15615   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15616   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15617
15618   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15619   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15620
15621   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15622   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15623   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15624
15625   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15626   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15627
15628   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15629   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15630   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15631   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15632   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15633
15634   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15635   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15636
15637 #if 0
15638   ListNode *node = engine_snapshot_list_rnd;
15639   int num_bytes = 0;
15640
15641   while (node != NULL)
15642   {
15643     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15644
15645     node = node->next;
15646   }
15647
15648   Debug("game:playing:SaveEngineSnapshotBuffers",
15649         "size of engine snapshot: %d bytes", num_bytes);
15650 #endif
15651
15652   return buffers;
15653 }
15654
15655 void SaveEngineSnapshotSingle(void)
15656 {
15657   ListNode *buffers = SaveEngineSnapshotBuffers();
15658
15659   // finally save all snapshot buffers to single snapshot
15660   SaveSnapshotSingle(buffers);
15661
15662   // save level identification information
15663   setString(&snapshot_level_identifier, leveldir_current->identifier);
15664   snapshot_level_nr = level_nr;
15665 }
15666
15667 boolean CheckSaveEngineSnapshotToList(void)
15668 {
15669   boolean save_snapshot =
15670     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15671      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15672       game.snapshot.changed_action) ||
15673      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15674       game.snapshot.collected_item));
15675
15676   game.snapshot.changed_action = FALSE;
15677   game.snapshot.collected_item = FALSE;
15678   game.snapshot.save_snapshot = save_snapshot;
15679
15680   return save_snapshot;
15681 }
15682
15683 void SaveEngineSnapshotToList(void)
15684 {
15685   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15686       tape.quick_resume)
15687     return;
15688
15689   ListNode *buffers = SaveEngineSnapshotBuffers();
15690
15691   // finally save all snapshot buffers to snapshot list
15692   SaveSnapshotToList(buffers);
15693 }
15694
15695 void SaveEngineSnapshotToListInitial(void)
15696 {
15697   FreeEngineSnapshotList();
15698
15699   SaveEngineSnapshotToList();
15700 }
15701
15702 static void LoadEngineSnapshotValues(void)
15703 {
15704   // restore special values from snapshot structure
15705
15706   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15707     LoadEngineSnapshotValues_RND();
15708   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15709     LoadEngineSnapshotValues_EM();
15710   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15711     LoadEngineSnapshotValues_SP();
15712   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15713     LoadEngineSnapshotValues_MM();
15714 }
15715
15716 void LoadEngineSnapshotSingle(void)
15717 {
15718   LoadSnapshotSingle();
15719
15720   LoadEngineSnapshotValues();
15721 }
15722
15723 static void LoadEngineSnapshot_Undo(int steps)
15724 {
15725   LoadSnapshotFromList_Older(steps);
15726
15727   LoadEngineSnapshotValues();
15728 }
15729
15730 static void LoadEngineSnapshot_Redo(int steps)
15731 {
15732   LoadSnapshotFromList_Newer(steps);
15733
15734   LoadEngineSnapshotValues();
15735 }
15736
15737 boolean CheckEngineSnapshotSingle(void)
15738 {
15739   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15740           snapshot_level_nr == level_nr);
15741 }
15742
15743 boolean CheckEngineSnapshotList(void)
15744 {
15745   return CheckSnapshotList();
15746 }
15747
15748
15749 // ---------- new game button stuff -------------------------------------------
15750
15751 static struct
15752 {
15753   int graphic;
15754   struct XY *pos;
15755   int gadget_id;
15756   boolean *setup_value;
15757   boolean allowed_on_tape;
15758   boolean is_touch_button;
15759   char *infotext;
15760 } gamebutton_info[NUM_GAME_BUTTONS] =
15761 {
15762   {
15763     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15764     GAME_CTRL_ID_STOP,                          NULL,
15765     TRUE, FALSE,                                "stop game"
15766   },
15767   {
15768     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15769     GAME_CTRL_ID_PAUSE,                         NULL,
15770     TRUE, FALSE,                                "pause game"
15771   },
15772   {
15773     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15774     GAME_CTRL_ID_PLAY,                          NULL,
15775     TRUE, FALSE,                                "play game"
15776   },
15777   {
15778     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15779     GAME_CTRL_ID_UNDO,                          NULL,
15780     TRUE, FALSE,                                "undo step"
15781   },
15782   {
15783     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15784     GAME_CTRL_ID_REDO,                          NULL,
15785     TRUE, FALSE,                                "redo step"
15786   },
15787   {
15788     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15789     GAME_CTRL_ID_SAVE,                          NULL,
15790     TRUE, FALSE,                                "save game"
15791   },
15792   {
15793     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15794     GAME_CTRL_ID_PAUSE2,                        NULL,
15795     TRUE, FALSE,                                "pause game"
15796   },
15797   {
15798     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15799     GAME_CTRL_ID_LOAD,                          NULL,
15800     TRUE, FALSE,                                "load game"
15801   },
15802   {
15803     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15804     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15805     FALSE, FALSE,                               "stop game"
15806   },
15807   {
15808     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15809     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15810     FALSE, FALSE,                               "pause game"
15811   },
15812   {
15813     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15814     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15815     FALSE, FALSE,                               "play game"
15816   },
15817   {
15818     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
15819     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
15820     FALSE, TRUE,                                "stop game"
15821   },
15822   {
15823     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
15824     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
15825     FALSE, TRUE,                                "pause game"
15826   },
15827   {
15828     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15829     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15830     TRUE, FALSE,                                "background music on/off"
15831   },
15832   {
15833     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15834     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15835     TRUE, FALSE,                                "sound loops on/off"
15836   },
15837   {
15838     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15839     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15840     TRUE, FALSE,                                "normal sounds on/off"
15841   },
15842   {
15843     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15844     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15845     FALSE, FALSE,                               "background music on/off"
15846   },
15847   {
15848     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15849     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15850     FALSE, FALSE,                               "sound loops on/off"
15851   },
15852   {
15853     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15854     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15855     FALSE, FALSE,                               "normal sounds on/off"
15856   }
15857 };
15858
15859 void CreateGameButtons(void)
15860 {
15861   int i;
15862
15863   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15864   {
15865     int graphic = gamebutton_info[i].graphic;
15866     struct GraphicInfo *gfx = &graphic_info[graphic];
15867     struct XY *pos = gamebutton_info[i].pos;
15868     struct GadgetInfo *gi;
15869     int button_type;
15870     boolean checked;
15871     unsigned int event_mask;
15872     boolean is_touch_button = gamebutton_info[i].is_touch_button;
15873     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15874     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15875     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
15876     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
15877     int gd_x   = gfx->src_x;
15878     int gd_y   = gfx->src_y;
15879     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15880     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15881     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15882     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15883     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15884     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15885     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
15886     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
15887     int id = i;
15888
15889     if (gfx->bitmap == NULL)
15890     {
15891       game_gadget[id] = NULL;
15892
15893       continue;
15894     }
15895
15896     if (id == GAME_CTRL_ID_STOP ||
15897         id == GAME_CTRL_ID_PANEL_STOP ||
15898         id == GAME_CTRL_ID_TOUCH_STOP ||
15899         id == GAME_CTRL_ID_PLAY ||
15900         id == GAME_CTRL_ID_PANEL_PLAY ||
15901         id == GAME_CTRL_ID_SAVE ||
15902         id == GAME_CTRL_ID_LOAD)
15903     {
15904       button_type = GD_TYPE_NORMAL_BUTTON;
15905       checked = FALSE;
15906       event_mask = GD_EVENT_RELEASED;
15907     }
15908     else if (id == GAME_CTRL_ID_UNDO ||
15909              id == GAME_CTRL_ID_REDO)
15910     {
15911       button_type = GD_TYPE_NORMAL_BUTTON;
15912       checked = FALSE;
15913       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15914     }
15915     else
15916     {
15917       button_type = GD_TYPE_CHECK_BUTTON;
15918       checked = (gamebutton_info[i].setup_value != NULL ?
15919                  *gamebutton_info[i].setup_value : FALSE);
15920       event_mask = GD_EVENT_PRESSED;
15921     }
15922
15923     gi = CreateGadget(GDI_CUSTOM_ID, id,
15924                       GDI_IMAGE_ID, graphic,
15925                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15926                       GDI_X, base_x + x,
15927                       GDI_Y, base_y + y,
15928                       GDI_WIDTH, gfx->width,
15929                       GDI_HEIGHT, gfx->height,
15930                       GDI_TYPE, button_type,
15931                       GDI_STATE, GD_BUTTON_UNPRESSED,
15932                       GDI_CHECKED, checked,
15933                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15934                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15935                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15936                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15937                       GDI_DIRECT_DRAW, FALSE,
15938                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
15939                       GDI_EVENT_MASK, event_mask,
15940                       GDI_CALLBACK_ACTION, HandleGameButtons,
15941                       GDI_END);
15942
15943     if (gi == NULL)
15944       Fail("cannot create gadget");
15945
15946     game_gadget[id] = gi;
15947   }
15948 }
15949
15950 void FreeGameButtons(void)
15951 {
15952   int i;
15953
15954   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15955     FreeGadget(game_gadget[i]);
15956 }
15957
15958 static void UnmapGameButtonsAtSamePosition(int id)
15959 {
15960   int i;
15961
15962   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15963     if (i != id &&
15964         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15965         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15966       UnmapGadget(game_gadget[i]);
15967 }
15968
15969 static void UnmapGameButtonsAtSamePosition_All(void)
15970 {
15971   if (setup.show_snapshot_buttons)
15972   {
15973     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15974     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15975     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15976   }
15977   else
15978   {
15979     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15980     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15981     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15982
15983     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15984     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15985     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15986   }
15987 }
15988
15989 static void MapGameButtonsAtSamePosition(int id)
15990 {
15991   int i;
15992
15993   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15994     if (i != id &&
15995         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15996         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15997       MapGadget(game_gadget[i]);
15998
15999   UnmapGameButtonsAtSamePosition_All();
16000 }
16001
16002 void MapUndoRedoButtons(void)
16003 {
16004   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16005   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16006
16007   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16008   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16009 }
16010
16011 void UnmapUndoRedoButtons(void)
16012 {
16013   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16014   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16015
16016   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16017   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16018 }
16019
16020 void ModifyPauseButtons(void)
16021 {
16022   static int ids[] =
16023   {
16024     GAME_CTRL_ID_PAUSE,
16025     GAME_CTRL_ID_PAUSE2,
16026     GAME_CTRL_ID_PANEL_PAUSE,
16027     GAME_CTRL_ID_TOUCH_PAUSE,
16028     -1
16029   };
16030   int i;
16031
16032   for (i = 0; ids[i] > -1; i++)
16033     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16034 }
16035
16036 static void MapGameButtonsExt(boolean on_tape)
16037 {
16038   int i;
16039
16040   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16041     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
16042         i != GAME_CTRL_ID_UNDO &&
16043         i != GAME_CTRL_ID_REDO)
16044       MapGadget(game_gadget[i]);
16045
16046   UnmapGameButtonsAtSamePosition_All();
16047
16048   RedrawGameButtons();
16049 }
16050
16051 static void UnmapGameButtonsExt(boolean on_tape)
16052 {
16053   int i;
16054
16055   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16056     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16057       UnmapGadget(game_gadget[i]);
16058 }
16059
16060 static void RedrawGameButtonsExt(boolean on_tape)
16061 {
16062   int i;
16063
16064   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16065     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16066       RedrawGadget(game_gadget[i]);
16067 }
16068
16069 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16070 {
16071   if (gi == NULL)
16072     return;
16073
16074   gi->checked = state;
16075 }
16076
16077 static void RedrawSoundButtonGadget(int id)
16078 {
16079   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16080              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16081              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16082              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16083              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16084              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16085              id);
16086
16087   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16088   RedrawGadget(game_gadget[id2]);
16089 }
16090
16091 void MapGameButtons(void)
16092 {
16093   MapGameButtonsExt(FALSE);
16094 }
16095
16096 void UnmapGameButtons(void)
16097 {
16098   UnmapGameButtonsExt(FALSE);
16099 }
16100
16101 void RedrawGameButtons(void)
16102 {
16103   RedrawGameButtonsExt(FALSE);
16104 }
16105
16106 void MapGameButtonsOnTape(void)
16107 {
16108   MapGameButtonsExt(TRUE);
16109 }
16110
16111 void UnmapGameButtonsOnTape(void)
16112 {
16113   UnmapGameButtonsExt(TRUE);
16114 }
16115
16116 void RedrawGameButtonsOnTape(void)
16117 {
16118   RedrawGameButtonsExt(TRUE);
16119 }
16120
16121 static void GameUndoRedoExt(void)
16122 {
16123   ClearPlayerAction();
16124
16125   tape.pausing = TRUE;
16126
16127   RedrawPlayfield();
16128   UpdateAndDisplayGameControlValues();
16129
16130   DrawCompleteVideoDisplay();
16131   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16132   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16133   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16134
16135   BackToFront();
16136 }
16137
16138 static void GameUndo(int steps)
16139 {
16140   if (!CheckEngineSnapshotList())
16141     return;
16142
16143   LoadEngineSnapshot_Undo(steps);
16144
16145   GameUndoRedoExt();
16146 }
16147
16148 static void GameRedo(int steps)
16149 {
16150   if (!CheckEngineSnapshotList())
16151     return;
16152
16153   LoadEngineSnapshot_Redo(steps);
16154
16155   GameUndoRedoExt();
16156 }
16157
16158 static void HandleGameButtonsExt(int id, int button)
16159 {
16160   static boolean game_undo_executed = FALSE;
16161   int steps = BUTTON_STEPSIZE(button);
16162   boolean handle_game_buttons =
16163     (game_status == GAME_MODE_PLAYING ||
16164      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16165
16166   if (!handle_game_buttons)
16167     return;
16168
16169   switch (id)
16170   {
16171     case GAME_CTRL_ID_STOP:
16172     case GAME_CTRL_ID_PANEL_STOP:
16173     case GAME_CTRL_ID_TOUCH_STOP:
16174       if (game_status == GAME_MODE_MAIN)
16175         break;
16176
16177       if (tape.playing)
16178         TapeStop();
16179       else
16180         RequestQuitGame(TRUE);
16181
16182       break;
16183
16184     case GAME_CTRL_ID_PAUSE:
16185     case GAME_CTRL_ID_PAUSE2:
16186     case GAME_CTRL_ID_PANEL_PAUSE:
16187     case GAME_CTRL_ID_TOUCH_PAUSE:
16188       if (network.enabled && game_status == GAME_MODE_PLAYING)
16189       {
16190         if (tape.pausing)
16191           SendToServer_ContinuePlaying();
16192         else
16193           SendToServer_PausePlaying();
16194       }
16195       else
16196         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16197
16198       game_undo_executed = FALSE;
16199
16200       break;
16201
16202     case GAME_CTRL_ID_PLAY:
16203     case GAME_CTRL_ID_PANEL_PLAY:
16204       if (game_status == GAME_MODE_MAIN)
16205       {
16206         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16207       }
16208       else if (tape.pausing)
16209       {
16210         if (network.enabled)
16211           SendToServer_ContinuePlaying();
16212         else
16213           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16214       }
16215       break;
16216
16217     case GAME_CTRL_ID_UNDO:
16218       // Important: When using "save snapshot when collecting an item" mode,
16219       // load last (current) snapshot for first "undo" after pressing "pause"
16220       // (else the last-but-one snapshot would be loaded, because the snapshot
16221       // pointer already points to the last snapshot when pressing "pause",
16222       // which is fine for "every step/move" mode, but not for "every collect")
16223       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16224           !game_undo_executed)
16225         steps--;
16226
16227       game_undo_executed = TRUE;
16228
16229       GameUndo(steps);
16230       break;
16231
16232     case GAME_CTRL_ID_REDO:
16233       GameRedo(steps);
16234       break;
16235
16236     case GAME_CTRL_ID_SAVE:
16237       TapeQuickSave();
16238       break;
16239
16240     case GAME_CTRL_ID_LOAD:
16241       TapeQuickLoad();
16242       break;
16243
16244     case SOUND_CTRL_ID_MUSIC:
16245     case SOUND_CTRL_ID_PANEL_MUSIC:
16246       if (setup.sound_music)
16247       { 
16248         setup.sound_music = FALSE;
16249
16250         FadeMusic();
16251       }
16252       else if (audio.music_available)
16253       { 
16254         setup.sound = setup.sound_music = TRUE;
16255
16256         SetAudioMode(setup.sound);
16257
16258         if (game_status == GAME_MODE_PLAYING)
16259           PlayLevelMusic();
16260       }
16261
16262       RedrawSoundButtonGadget(id);
16263
16264       break;
16265
16266     case SOUND_CTRL_ID_LOOPS:
16267     case SOUND_CTRL_ID_PANEL_LOOPS:
16268       if (setup.sound_loops)
16269         setup.sound_loops = FALSE;
16270       else if (audio.loops_available)
16271       {
16272         setup.sound = setup.sound_loops = TRUE;
16273
16274         SetAudioMode(setup.sound);
16275       }
16276
16277       RedrawSoundButtonGadget(id);
16278
16279       break;
16280
16281     case SOUND_CTRL_ID_SIMPLE:
16282     case SOUND_CTRL_ID_PANEL_SIMPLE:
16283       if (setup.sound_simple)
16284         setup.sound_simple = FALSE;
16285       else if (audio.sound_available)
16286       {
16287         setup.sound = setup.sound_simple = TRUE;
16288
16289         SetAudioMode(setup.sound);
16290       }
16291
16292       RedrawSoundButtonGadget(id);
16293
16294       break;
16295
16296     default:
16297       break;
16298   }
16299 }
16300
16301 static void HandleGameButtons(struct GadgetInfo *gi)
16302 {
16303   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16304 }
16305
16306 void HandleSoundButtonKeys(Key key)
16307 {
16308   if (key == setup.shortcut.sound_simple)
16309     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16310   else if (key == setup.shortcut.sound_loops)
16311     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16312   else if (key == setup.shortcut.sound_music)
16313     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16314 }