cb107f0e8e7aa0803b5240eab1acdfd89fe1597b
[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   // initialize tape actions from game when recording tape
3580   if (tape.recording)
3581   {
3582     tape.use_key_actions   = game.use_key_actions;
3583     tape.use_mouse_actions = game.use_mouse_actions;
3584   }
3585
3586   // don't play tapes over network
3587   network_playing = (network.enabled && !tape.playing);
3588
3589   for (i = 0; i < MAX_PLAYERS; i++)
3590   {
3591     struct PlayerInfo *player = &stored_player[i];
3592
3593     player->index_nr = i;
3594     player->index_bit = (1 << i);
3595     player->element_nr = EL_PLAYER_1 + i;
3596
3597     player->present = FALSE;
3598     player->active = FALSE;
3599     player->mapped = FALSE;
3600
3601     player->killed = FALSE;
3602     player->reanimated = FALSE;
3603     player->buried = FALSE;
3604
3605     player->action = 0;
3606     player->effective_action = 0;
3607     player->programmed_action = 0;
3608     player->snap_action = 0;
3609
3610     player->mouse_action.lx = 0;
3611     player->mouse_action.ly = 0;
3612     player->mouse_action.button = 0;
3613     player->mouse_action.button_hint = 0;
3614
3615     player->effective_mouse_action.lx = 0;
3616     player->effective_mouse_action.ly = 0;
3617     player->effective_mouse_action.button = 0;
3618     player->effective_mouse_action.button_hint = 0;
3619
3620     for (j = 0; j < MAX_NUM_KEYS; j++)
3621       player->key[j] = FALSE;
3622
3623     player->num_white_keys = 0;
3624
3625     player->dynabomb_count = 0;
3626     player->dynabomb_size = 1;
3627     player->dynabombs_left = 0;
3628     player->dynabomb_xl = FALSE;
3629
3630     player->MovDir = initial_move_dir;
3631     player->MovPos = 0;
3632     player->GfxPos = 0;
3633     player->GfxDir = initial_move_dir;
3634     player->GfxAction = ACTION_DEFAULT;
3635     player->Frame = 0;
3636     player->StepFrame = 0;
3637
3638     player->initial_element = player->element_nr;
3639     player->artwork_element =
3640       (level.use_artwork_element[i] ? level.artwork_element[i] :
3641        player->element_nr);
3642     player->use_murphy = FALSE;
3643
3644     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3645     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3646
3647     player->gravity = level.initial_player_gravity[i];
3648
3649     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3650
3651     player->actual_frame_counter = 0;
3652
3653     player->step_counter = 0;
3654
3655     player->last_move_dir = initial_move_dir;
3656
3657     player->is_active = FALSE;
3658
3659     player->is_waiting = FALSE;
3660     player->is_moving = FALSE;
3661     player->is_auto_moving = FALSE;
3662     player->is_digging = FALSE;
3663     player->is_snapping = FALSE;
3664     player->is_collecting = FALSE;
3665     player->is_pushing = FALSE;
3666     player->is_switching = FALSE;
3667     player->is_dropping = FALSE;
3668     player->is_dropping_pressed = FALSE;
3669
3670     player->is_bored = FALSE;
3671     player->is_sleeping = FALSE;
3672
3673     player->was_waiting = TRUE;
3674     player->was_moving = FALSE;
3675     player->was_snapping = FALSE;
3676     player->was_dropping = FALSE;
3677
3678     player->force_dropping = FALSE;
3679
3680     player->frame_counter_bored = -1;
3681     player->frame_counter_sleeping = -1;
3682
3683     player->anim_delay_counter = 0;
3684     player->post_delay_counter = 0;
3685
3686     player->dir_waiting = initial_move_dir;
3687     player->action_waiting = ACTION_DEFAULT;
3688     player->last_action_waiting = ACTION_DEFAULT;
3689     player->special_action_bored = ACTION_DEFAULT;
3690     player->special_action_sleeping = ACTION_DEFAULT;
3691
3692     player->switch_x = -1;
3693     player->switch_y = -1;
3694
3695     player->drop_x = -1;
3696     player->drop_y = -1;
3697
3698     player->show_envelope = 0;
3699
3700     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3701
3702     player->push_delay       = -1;      // initialized when pushing starts
3703     player->push_delay_value = game.initial_push_delay_value;
3704
3705     player->drop_delay = 0;
3706     player->drop_pressed_delay = 0;
3707
3708     player->last_jx = -1;
3709     player->last_jy = -1;
3710     player->jx = -1;
3711     player->jy = -1;
3712
3713     player->shield_normal_time_left = 0;
3714     player->shield_deadly_time_left = 0;
3715
3716     player->inventory_infinite_element = EL_UNDEFINED;
3717     player->inventory_size = 0;
3718
3719     if (level.use_initial_inventory[i])
3720     {
3721       for (j = 0; j < level.initial_inventory_size[i]; j++)
3722       {
3723         int element = level.initial_inventory_content[i][j];
3724         int collect_count = element_info[element].collect_count_initial;
3725         int k;
3726
3727         if (!IS_CUSTOM_ELEMENT(element))
3728           collect_count = 1;
3729
3730         if (collect_count == 0)
3731           player->inventory_infinite_element = element;
3732         else
3733           for (k = 0; k < collect_count; k++)
3734             if (player->inventory_size < MAX_INVENTORY_SIZE)
3735               player->inventory_element[player->inventory_size++] = element;
3736       }
3737     }
3738
3739     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3740     SnapField(player, 0, 0);
3741
3742     map_player_action[i] = i;
3743   }
3744
3745   network_player_action_received = FALSE;
3746
3747   // initial null action
3748   if (network_playing)
3749     SendToServer_MovePlayer(MV_NONE);
3750
3751   FrameCounter = 0;
3752   TimeFrames = 0;
3753   TimePlayed = 0;
3754   TimeLeft = level.time;
3755   TapeTime = 0;
3756
3757   ScreenMovDir = MV_NONE;
3758   ScreenMovPos = 0;
3759   ScreenGfxPos = 0;
3760
3761   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3762
3763   game.robot_wheel_x = -1;
3764   game.robot_wheel_y = -1;
3765
3766   game.exit_x = -1;
3767   game.exit_y = -1;
3768
3769   game.all_players_gone = FALSE;
3770
3771   game.LevelSolved = FALSE;
3772   game.GameOver = FALSE;
3773
3774   game.GamePlayed = !tape.playing;
3775
3776   game.LevelSolved_GameWon = FALSE;
3777   game.LevelSolved_GameEnd = FALSE;
3778   game.LevelSolved_SaveTape = FALSE;
3779   game.LevelSolved_SaveScore = FALSE;
3780
3781   game.LevelSolved_CountingTime = 0;
3782   game.LevelSolved_CountingScore = 0;
3783   game.LevelSolved_CountingHealth = 0;
3784
3785   game.panel.active = TRUE;
3786
3787   game.no_time_limit = (level.time == 0);
3788
3789   game.yamyam_content_nr = 0;
3790   game.robot_wheel_active = FALSE;
3791   game.magic_wall_active = FALSE;
3792   game.magic_wall_time_left = 0;
3793   game.light_time_left = 0;
3794   game.timegate_time_left = 0;
3795   game.switchgate_pos = 0;
3796   game.wind_direction = level.wind_direction_initial;
3797
3798   game.score = 0;
3799   game.score_final = 0;
3800
3801   game.health = MAX_HEALTH;
3802   game.health_final = MAX_HEALTH;
3803
3804   game.gems_still_needed = level.gems_needed;
3805   game.sokoban_fields_still_needed = 0;
3806   game.sokoban_objects_still_needed = 0;
3807   game.lights_still_needed = 0;
3808   game.players_still_needed = 0;
3809   game.friends_still_needed = 0;
3810
3811   game.lenses_time_left = 0;
3812   game.magnify_time_left = 0;
3813
3814   game.ball_active = level.ball_active_initial;
3815   game.ball_content_nr = 0;
3816
3817   game.explosions_delayed = TRUE;
3818
3819   game.envelope_active = FALSE;
3820
3821   for (i = 0; i < NUM_BELTS; i++)
3822   {
3823     game.belt_dir[i] = MV_NONE;
3824     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3825   }
3826
3827   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3828     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3829
3830 #if DEBUG_INIT_PLAYER
3831   DebugPrintPlayerStatus("Player status at level initialization");
3832 #endif
3833
3834   SCAN_PLAYFIELD(x, y)
3835   {
3836     Tile[x][y] = Last[x][y] = level.field[x][y];
3837     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3838     ChangeDelay[x][y] = 0;
3839     ChangePage[x][y] = -1;
3840     CustomValue[x][y] = 0;              // initialized in InitField()
3841     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3842     AmoebaNr[x][y] = 0;
3843     WasJustMoving[x][y] = 0;
3844     WasJustFalling[x][y] = 0;
3845     CheckCollision[x][y] = 0;
3846     CheckImpact[x][y] = 0;
3847     Stop[x][y] = FALSE;
3848     Pushed[x][y] = FALSE;
3849
3850     ChangeCount[x][y] = 0;
3851     ChangeEvent[x][y] = -1;
3852
3853     ExplodePhase[x][y] = 0;
3854     ExplodeDelay[x][y] = 0;
3855     ExplodeField[x][y] = EX_TYPE_NONE;
3856
3857     RunnerVisit[x][y] = 0;
3858     PlayerVisit[x][y] = 0;
3859
3860     GfxFrame[x][y] = 0;
3861     GfxRandom[x][y] = INIT_GFX_RANDOM();
3862     GfxElement[x][y] = EL_UNDEFINED;
3863     GfxAction[x][y] = ACTION_DEFAULT;
3864     GfxDir[x][y] = MV_NONE;
3865     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3866   }
3867
3868   SCAN_PLAYFIELD(x, y)
3869   {
3870     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3871       emulate_bd = FALSE;
3872     if (emulate_sb && !IS_SB_ELEMENT(Tile[x][y]))
3873       emulate_sb = FALSE;
3874     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3875       emulate_sp = FALSE;
3876
3877     InitField(x, y, TRUE);
3878
3879     ResetGfxAnimation(x, y);
3880   }
3881
3882   InitBeltMovement();
3883
3884   for (i = 0; i < MAX_PLAYERS; i++)
3885   {
3886     struct PlayerInfo *player = &stored_player[i];
3887
3888     // set number of special actions for bored and sleeping animation
3889     player->num_special_action_bored =
3890       get_num_special_action(player->artwork_element,
3891                              ACTION_BORING_1, ACTION_BORING_LAST);
3892     player->num_special_action_sleeping =
3893       get_num_special_action(player->artwork_element,
3894                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3895   }
3896
3897   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3898                     emulate_sb ? EMU_SOKOBAN :
3899                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3900
3901   // initialize type of slippery elements
3902   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3903   {
3904     if (!IS_CUSTOM_ELEMENT(i))
3905     {
3906       // default: elements slip down either to the left or right randomly
3907       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3908
3909       // SP style elements prefer to slip down on the left side
3910       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3911         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3912
3913       // BD style elements prefer to slip down on the left side
3914       if (game.emulation == EMU_BOULDERDASH)
3915         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3916     }
3917   }
3918
3919   // initialize explosion and ignition delay
3920   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3921   {
3922     if (!IS_CUSTOM_ELEMENT(i))
3923     {
3924       int num_phase = 8;
3925       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3926                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3927                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3928       int last_phase = (num_phase + 1) * delay;
3929       int half_phase = (num_phase / 2) * delay;
3930
3931       element_info[i].explosion_delay = last_phase - 1;
3932       element_info[i].ignition_delay = half_phase;
3933
3934       if (i == EL_BLACK_ORB)
3935         element_info[i].ignition_delay = 1;
3936     }
3937   }
3938
3939   // correct non-moving belts to start moving left
3940   for (i = 0; i < NUM_BELTS; i++)
3941     if (game.belt_dir[i] == MV_NONE)
3942       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3943
3944 #if USE_NEW_PLAYER_ASSIGNMENTS
3945   // use preferred player also in local single-player mode
3946   if (!network.enabled && !game.team_mode)
3947   {
3948     int new_index_nr = setup.network_player_nr;
3949
3950     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3951     {
3952       for (i = 0; i < MAX_PLAYERS; i++)
3953         stored_player[i].connected_locally = FALSE;
3954
3955       stored_player[new_index_nr].connected_locally = TRUE;
3956     }
3957   }
3958
3959   for (i = 0; i < MAX_PLAYERS; i++)
3960   {
3961     stored_player[i].connected = FALSE;
3962
3963     // in network game mode, the local player might not be the first player
3964     if (stored_player[i].connected_locally)
3965       local_player = &stored_player[i];
3966   }
3967
3968   if (!network.enabled)
3969     local_player->connected = TRUE;
3970
3971   if (tape.playing)
3972   {
3973     for (i = 0; i < MAX_PLAYERS; i++)
3974       stored_player[i].connected = tape.player_participates[i];
3975   }
3976   else if (network.enabled)
3977   {
3978     // add team mode players connected over the network (needed for correct
3979     // assignment of player figures from level to locally playing players)
3980
3981     for (i = 0; i < MAX_PLAYERS; i++)
3982       if (stored_player[i].connected_network)
3983         stored_player[i].connected = TRUE;
3984   }
3985   else if (game.team_mode)
3986   {
3987     // try to guess locally connected team mode players (needed for correct
3988     // assignment of player figures from level to locally playing players)
3989
3990     for (i = 0; i < MAX_PLAYERS; i++)
3991       if (setup.input[i].use_joystick ||
3992           setup.input[i].key.left != KSYM_UNDEFINED)
3993         stored_player[i].connected = TRUE;
3994   }
3995
3996 #if DEBUG_INIT_PLAYER
3997   DebugPrintPlayerStatus("Player status after level initialization");
3998 #endif
3999
4000 #if DEBUG_INIT_PLAYER
4001   Debug("game:init:player", "Reassigning players ...");
4002 #endif
4003
4004   // check if any connected player was not found in playfield
4005   for (i = 0; i < MAX_PLAYERS; i++)
4006   {
4007     struct PlayerInfo *player = &stored_player[i];
4008
4009     if (player->connected && !player->present)
4010     {
4011       struct PlayerInfo *field_player = NULL;
4012
4013 #if DEBUG_INIT_PLAYER
4014       Debug("game:init:player",
4015             "- looking for field player for player %d ...", i + 1);
4016 #endif
4017
4018       // assign first free player found that is present in the playfield
4019
4020       // first try: look for unmapped playfield player that is not connected
4021       for (j = 0; j < MAX_PLAYERS; j++)
4022         if (field_player == NULL &&
4023             stored_player[j].present &&
4024             !stored_player[j].mapped &&
4025             !stored_player[j].connected)
4026           field_player = &stored_player[j];
4027
4028       // second try: look for *any* unmapped playfield player
4029       for (j = 0; j < MAX_PLAYERS; j++)
4030         if (field_player == NULL &&
4031             stored_player[j].present &&
4032             !stored_player[j].mapped)
4033           field_player = &stored_player[j];
4034
4035       if (field_player != NULL)
4036       {
4037         int jx = field_player->jx, jy = field_player->jy;
4038
4039 #if DEBUG_INIT_PLAYER
4040         Debug("game:init:player", "- found player %d",
4041               field_player->index_nr + 1);
4042 #endif
4043
4044         player->present = FALSE;
4045         player->active = FALSE;
4046
4047         field_player->present = TRUE;
4048         field_player->active = TRUE;
4049
4050         /*
4051         player->initial_element = field_player->initial_element;
4052         player->artwork_element = field_player->artwork_element;
4053
4054         player->block_last_field       = field_player->block_last_field;
4055         player->block_delay_adjustment = field_player->block_delay_adjustment;
4056         */
4057
4058         StorePlayer[jx][jy] = field_player->element_nr;
4059
4060         field_player->jx = field_player->last_jx = jx;
4061         field_player->jy = field_player->last_jy = jy;
4062
4063         if (local_player == player)
4064           local_player = field_player;
4065
4066         map_player_action[field_player->index_nr] = i;
4067
4068         field_player->mapped = TRUE;
4069
4070 #if DEBUG_INIT_PLAYER
4071         Debug("game:init:player", "- map_player_action[%d] == %d",
4072               field_player->index_nr + 1, i + 1);
4073 #endif
4074       }
4075     }
4076
4077     if (player->connected && player->present)
4078       player->mapped = TRUE;
4079   }
4080
4081 #if DEBUG_INIT_PLAYER
4082   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4083 #endif
4084
4085 #else
4086
4087   // check if any connected player was not found in playfield
4088   for (i = 0; i < MAX_PLAYERS; i++)
4089   {
4090     struct PlayerInfo *player = &stored_player[i];
4091
4092     if (player->connected && !player->present)
4093     {
4094       for (j = 0; j < MAX_PLAYERS; j++)
4095       {
4096         struct PlayerInfo *field_player = &stored_player[j];
4097         int jx = field_player->jx, jy = field_player->jy;
4098
4099         // assign first free player found that is present in the playfield
4100         if (field_player->present && !field_player->connected)
4101         {
4102           player->present = TRUE;
4103           player->active = TRUE;
4104
4105           field_player->present = FALSE;
4106           field_player->active = FALSE;
4107
4108           player->initial_element = field_player->initial_element;
4109           player->artwork_element = field_player->artwork_element;
4110
4111           player->block_last_field       = field_player->block_last_field;
4112           player->block_delay_adjustment = field_player->block_delay_adjustment;
4113
4114           StorePlayer[jx][jy] = player->element_nr;
4115
4116           player->jx = player->last_jx = jx;
4117           player->jy = player->last_jy = jy;
4118
4119           break;
4120         }
4121       }
4122     }
4123   }
4124 #endif
4125
4126 #if 0
4127   Debug("game:init:player", "local_player->present == %d",
4128         local_player->present);
4129 #endif
4130
4131   // set focus to local player for network games, else to all players
4132   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4133   game.centered_player_nr_next = game.centered_player_nr;
4134   game.set_centered_player = FALSE;
4135   game.set_centered_player_wrap = FALSE;
4136
4137   if (network_playing && tape.recording)
4138   {
4139     // store client dependent player focus when recording network games
4140     tape.centered_player_nr_next = game.centered_player_nr_next;
4141     tape.set_centered_player = TRUE;
4142   }
4143
4144   if (tape.playing)
4145   {
4146     // when playing a tape, eliminate all players who do not participate
4147
4148 #if USE_NEW_PLAYER_ASSIGNMENTS
4149
4150     if (!game.team_mode)
4151     {
4152       for (i = 0; i < MAX_PLAYERS; i++)
4153       {
4154         if (stored_player[i].active &&
4155             !tape.player_participates[map_player_action[i]])
4156         {
4157           struct PlayerInfo *player = &stored_player[i];
4158           int jx = player->jx, jy = player->jy;
4159
4160 #if DEBUG_INIT_PLAYER
4161           Debug("game:init:player", "Removing player %d at (%d, %d)",
4162                 i + 1, jx, jy);
4163 #endif
4164
4165           player->active = FALSE;
4166           StorePlayer[jx][jy] = 0;
4167           Tile[jx][jy] = EL_EMPTY;
4168         }
4169       }
4170     }
4171
4172 #else
4173
4174     for (i = 0; i < MAX_PLAYERS; i++)
4175     {
4176       if (stored_player[i].active &&
4177           !tape.player_participates[i])
4178       {
4179         struct PlayerInfo *player = &stored_player[i];
4180         int jx = player->jx, jy = player->jy;
4181
4182         player->active = FALSE;
4183         StorePlayer[jx][jy] = 0;
4184         Tile[jx][jy] = EL_EMPTY;
4185       }
4186     }
4187 #endif
4188   }
4189   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4190   {
4191     // when in single player mode, eliminate all but the local player
4192
4193     for (i = 0; i < MAX_PLAYERS; i++)
4194     {
4195       struct PlayerInfo *player = &stored_player[i];
4196
4197       if (player->active && player != local_player)
4198       {
4199         int jx = player->jx, jy = player->jy;
4200
4201         player->active = FALSE;
4202         player->present = FALSE;
4203
4204         StorePlayer[jx][jy] = 0;
4205         Tile[jx][jy] = EL_EMPTY;
4206       }
4207     }
4208   }
4209
4210   for (i = 0; i < MAX_PLAYERS; i++)
4211     if (stored_player[i].active)
4212       game.players_still_needed++;
4213
4214   if (level.solved_by_one_player)
4215     game.players_still_needed = 1;
4216
4217   // when recording the game, store which players take part in the game
4218   if (tape.recording)
4219   {
4220 #if USE_NEW_PLAYER_ASSIGNMENTS
4221     for (i = 0; i < MAX_PLAYERS; i++)
4222       if (stored_player[i].connected)
4223         tape.player_participates[i] = TRUE;
4224 #else
4225     for (i = 0; i < MAX_PLAYERS; i++)
4226       if (stored_player[i].active)
4227         tape.player_participates[i] = TRUE;
4228 #endif
4229   }
4230
4231 #if DEBUG_INIT_PLAYER
4232   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4233 #endif
4234
4235   if (BorderElement == EL_EMPTY)
4236   {
4237     SBX_Left = 0;
4238     SBX_Right = lev_fieldx - SCR_FIELDX;
4239     SBY_Upper = 0;
4240     SBY_Lower = lev_fieldy - SCR_FIELDY;
4241   }
4242   else
4243   {
4244     SBX_Left = -1;
4245     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4246     SBY_Upper = -1;
4247     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4248   }
4249
4250   if (full_lev_fieldx <= SCR_FIELDX)
4251     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4252   if (full_lev_fieldy <= SCR_FIELDY)
4253     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4254
4255   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4256     SBX_Left--;
4257   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4258     SBY_Upper--;
4259
4260   // if local player not found, look for custom element that might create
4261   // the player (make some assumptions about the right custom element)
4262   if (!local_player->present)
4263   {
4264     int start_x = 0, start_y = 0;
4265     int found_rating = 0;
4266     int found_element = EL_UNDEFINED;
4267     int player_nr = local_player->index_nr;
4268
4269     SCAN_PLAYFIELD(x, y)
4270     {
4271       int element = Tile[x][y];
4272       int content;
4273       int xx, yy;
4274       boolean is_player;
4275
4276       if (level.use_start_element[player_nr] &&
4277           level.start_element[player_nr] == element &&
4278           found_rating < 4)
4279       {
4280         start_x = x;
4281         start_y = y;
4282
4283         found_rating = 4;
4284         found_element = element;
4285       }
4286
4287       if (!IS_CUSTOM_ELEMENT(element))
4288         continue;
4289
4290       if (CAN_CHANGE(element))
4291       {
4292         for (i = 0; i < element_info[element].num_change_pages; i++)
4293         {
4294           // check for player created from custom element as single target
4295           content = element_info[element].change_page[i].target_element;
4296           is_player = ELEM_IS_PLAYER(content);
4297
4298           if (is_player && (found_rating < 3 ||
4299                             (found_rating == 3 && element < found_element)))
4300           {
4301             start_x = x;
4302             start_y = y;
4303
4304             found_rating = 3;
4305             found_element = element;
4306           }
4307         }
4308       }
4309
4310       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4311       {
4312         // check for player created from custom element as explosion content
4313         content = element_info[element].content.e[xx][yy];
4314         is_player = ELEM_IS_PLAYER(content);
4315
4316         if (is_player && (found_rating < 2 ||
4317                           (found_rating == 2 && element < found_element)))
4318         {
4319           start_x = x + xx - 1;
4320           start_y = y + yy - 1;
4321
4322           found_rating = 2;
4323           found_element = element;
4324         }
4325
4326         if (!CAN_CHANGE(element))
4327           continue;
4328
4329         for (i = 0; i < element_info[element].num_change_pages; i++)
4330         {
4331           // check for player created from custom element as extended target
4332           content =
4333             element_info[element].change_page[i].target_content.e[xx][yy];
4334
4335           is_player = ELEM_IS_PLAYER(content);
4336
4337           if (is_player && (found_rating < 1 ||
4338                             (found_rating == 1 && element < found_element)))
4339           {
4340             start_x = x + xx - 1;
4341             start_y = y + yy - 1;
4342
4343             found_rating = 1;
4344             found_element = element;
4345           }
4346         }
4347       }
4348     }
4349
4350     scroll_x = SCROLL_POSITION_X(start_x);
4351     scroll_y = SCROLL_POSITION_Y(start_y);
4352   }
4353   else
4354   {
4355     scroll_x = SCROLL_POSITION_X(local_player->jx);
4356     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4357   }
4358
4359   // !!! FIX THIS (START) !!!
4360   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4361   {
4362     InitGameEngine_EM();
4363   }
4364   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4365   {
4366     InitGameEngine_SP();
4367   }
4368   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4369   {
4370     InitGameEngine_MM();
4371   }
4372   else
4373   {
4374     DrawLevel(REDRAW_FIELD);
4375     DrawAllPlayers();
4376
4377     // after drawing the level, correct some elements
4378     if (game.timegate_time_left == 0)
4379       CloseAllOpenTimegates();
4380   }
4381
4382   // blit playfield from scroll buffer to normal back buffer for fading in
4383   BlitScreenToBitmap(backbuffer);
4384   // !!! FIX THIS (END) !!!
4385
4386   DrawMaskedBorder(fade_mask);
4387
4388   FadeIn(fade_mask);
4389
4390 #if 1
4391   // full screen redraw is required at this point in the following cases:
4392   // - special editor door undrawn when game was started from level editor
4393   // - drawing area (playfield) was changed and has to be removed completely
4394   redraw_mask = REDRAW_ALL;
4395   BackToFront();
4396 #endif
4397
4398   if (!game.restart_level)
4399   {
4400     // copy default game door content to main double buffer
4401
4402     // !!! CHECK AGAIN !!!
4403     SetPanelBackground();
4404     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4405     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4406   }
4407
4408   SetPanelBackground();
4409   SetDrawBackgroundMask(REDRAW_DOOR_1);
4410
4411   UpdateAndDisplayGameControlValues();
4412
4413   if (!game.restart_level)
4414   {
4415     UnmapGameButtons();
4416     UnmapTapeButtons();
4417
4418     FreeGameButtons();
4419     CreateGameButtons();
4420
4421     MapGameButtons();
4422     MapTapeButtons();
4423
4424     // copy actual game door content to door double buffer for OpenDoor()
4425     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4426
4427     OpenDoor(DOOR_OPEN_ALL);
4428
4429     KeyboardAutoRepeatOffUnlessAutoplay();
4430
4431 #if DEBUG_INIT_PLAYER
4432     DebugPrintPlayerStatus("Player status (final)");
4433 #endif
4434   }
4435
4436   UnmapAllGadgets();
4437
4438   MapGameButtons();
4439   MapTapeButtons();
4440
4441   if (!game.restart_level && !tape.playing)
4442   {
4443     LevelStats_incPlayed(level_nr);
4444
4445     SaveLevelSetup_SeriesInfo();
4446   }
4447
4448   game.restart_level = FALSE;
4449   game.restart_game_message = NULL;
4450
4451   game.request_active = FALSE;
4452   game.request_active_or_moving = FALSE;
4453
4454   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4455     InitGameActions_MM();
4456
4457   SaveEngineSnapshotToListInitial();
4458
4459   if (!game.restart_level)
4460   {
4461     PlaySound(SND_GAME_STARTING);
4462
4463     if (setup.sound_music)
4464       PlayLevelMusic();
4465   }
4466 }
4467
4468 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4469                         int actual_player_x, int actual_player_y)
4470 {
4471   // this is used for non-R'n'D game engines to update certain engine values
4472
4473   // needed to determine if sounds are played within the visible screen area
4474   scroll_x = actual_scroll_x;
4475   scroll_y = actual_scroll_y;
4476
4477   // needed to get player position for "follow finger" playing input method
4478   local_player->jx = actual_player_x;
4479   local_player->jy = actual_player_y;
4480 }
4481
4482 void InitMovDir(int x, int y)
4483 {
4484   int i, element = Tile[x][y];
4485   static int xy[4][2] =
4486   {
4487     {  0, +1 },
4488     { +1,  0 },
4489     {  0, -1 },
4490     { -1,  0 }
4491   };
4492   static int direction[3][4] =
4493   {
4494     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4495     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4496     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4497   };
4498
4499   switch (element)
4500   {
4501     case EL_BUG_RIGHT:
4502     case EL_BUG_UP:
4503     case EL_BUG_LEFT:
4504     case EL_BUG_DOWN:
4505       Tile[x][y] = EL_BUG;
4506       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4507       break;
4508
4509     case EL_SPACESHIP_RIGHT:
4510     case EL_SPACESHIP_UP:
4511     case EL_SPACESHIP_LEFT:
4512     case EL_SPACESHIP_DOWN:
4513       Tile[x][y] = EL_SPACESHIP;
4514       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4515       break;
4516
4517     case EL_BD_BUTTERFLY_RIGHT:
4518     case EL_BD_BUTTERFLY_UP:
4519     case EL_BD_BUTTERFLY_LEFT:
4520     case EL_BD_BUTTERFLY_DOWN:
4521       Tile[x][y] = EL_BD_BUTTERFLY;
4522       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4523       break;
4524
4525     case EL_BD_FIREFLY_RIGHT:
4526     case EL_BD_FIREFLY_UP:
4527     case EL_BD_FIREFLY_LEFT:
4528     case EL_BD_FIREFLY_DOWN:
4529       Tile[x][y] = EL_BD_FIREFLY;
4530       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4531       break;
4532
4533     case EL_PACMAN_RIGHT:
4534     case EL_PACMAN_UP:
4535     case EL_PACMAN_LEFT:
4536     case EL_PACMAN_DOWN:
4537       Tile[x][y] = EL_PACMAN;
4538       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4539       break;
4540
4541     case EL_YAMYAM_LEFT:
4542     case EL_YAMYAM_RIGHT:
4543     case EL_YAMYAM_UP:
4544     case EL_YAMYAM_DOWN:
4545       Tile[x][y] = EL_YAMYAM;
4546       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4547       break;
4548
4549     case EL_SP_SNIKSNAK:
4550       MovDir[x][y] = MV_UP;
4551       break;
4552
4553     case EL_SP_ELECTRON:
4554       MovDir[x][y] = MV_LEFT;
4555       break;
4556
4557     case EL_MOLE_LEFT:
4558     case EL_MOLE_RIGHT:
4559     case EL_MOLE_UP:
4560     case EL_MOLE_DOWN:
4561       Tile[x][y] = EL_MOLE;
4562       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4563       break;
4564
4565     case EL_SPRING_LEFT:
4566     case EL_SPRING_RIGHT:
4567       Tile[x][y] = EL_SPRING;
4568       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4569       break;
4570
4571     default:
4572       if (IS_CUSTOM_ELEMENT(element))
4573       {
4574         struct ElementInfo *ei = &element_info[element];
4575         int move_direction_initial = ei->move_direction_initial;
4576         int move_pattern = ei->move_pattern;
4577
4578         if (move_direction_initial == MV_START_PREVIOUS)
4579         {
4580           if (MovDir[x][y] != MV_NONE)
4581             return;
4582
4583           move_direction_initial = MV_START_AUTOMATIC;
4584         }
4585
4586         if (move_direction_initial == MV_START_RANDOM)
4587           MovDir[x][y] = 1 << RND(4);
4588         else if (move_direction_initial & MV_ANY_DIRECTION)
4589           MovDir[x][y] = move_direction_initial;
4590         else if (move_pattern == MV_ALL_DIRECTIONS ||
4591                  move_pattern == MV_TURNING_LEFT ||
4592                  move_pattern == MV_TURNING_RIGHT ||
4593                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4594                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4595                  move_pattern == MV_TURNING_RANDOM)
4596           MovDir[x][y] = 1 << RND(4);
4597         else if (move_pattern == MV_HORIZONTAL)
4598           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4599         else if (move_pattern == MV_VERTICAL)
4600           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4601         else if (move_pattern & MV_ANY_DIRECTION)
4602           MovDir[x][y] = element_info[element].move_pattern;
4603         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4604                  move_pattern == MV_ALONG_RIGHT_SIDE)
4605         {
4606           // use random direction as default start direction
4607           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4608             MovDir[x][y] = 1 << RND(4);
4609
4610           for (i = 0; i < NUM_DIRECTIONS; i++)
4611           {
4612             int x1 = x + xy[i][0];
4613             int y1 = y + xy[i][1];
4614
4615             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4616             {
4617               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4618                 MovDir[x][y] = direction[0][i];
4619               else
4620                 MovDir[x][y] = direction[1][i];
4621
4622               break;
4623             }
4624           }
4625         }                
4626       }
4627       else
4628       {
4629         MovDir[x][y] = 1 << RND(4);
4630
4631         if (element != EL_BUG &&
4632             element != EL_SPACESHIP &&
4633             element != EL_BD_BUTTERFLY &&
4634             element != EL_BD_FIREFLY)
4635           break;
4636
4637         for (i = 0; i < NUM_DIRECTIONS; i++)
4638         {
4639           int x1 = x + xy[i][0];
4640           int y1 = y + xy[i][1];
4641
4642           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4643           {
4644             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4645             {
4646               MovDir[x][y] = direction[0][i];
4647               break;
4648             }
4649             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4650                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4651             {
4652               MovDir[x][y] = direction[1][i];
4653               break;
4654             }
4655           }
4656         }
4657       }
4658       break;
4659   }
4660
4661   GfxDir[x][y] = MovDir[x][y];
4662 }
4663
4664 void InitAmoebaNr(int x, int y)
4665 {
4666   int i;
4667   int group_nr = AmoebaNeighbourNr(x, y);
4668
4669   if (group_nr == 0)
4670   {
4671     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4672     {
4673       if (AmoebaCnt[i] == 0)
4674       {
4675         group_nr = i;
4676         break;
4677       }
4678     }
4679   }
4680
4681   AmoebaNr[x][y] = group_nr;
4682   AmoebaCnt[group_nr]++;
4683   AmoebaCnt2[group_nr]++;
4684 }
4685
4686 static void LevelSolved(void)
4687 {
4688   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4689       game.players_still_needed > 0)
4690     return;
4691
4692   game.LevelSolved = TRUE;
4693   game.GameOver = TRUE;
4694
4695   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4696                       game_em.lev->score :
4697                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4698                       game_mm.score :
4699                       game.score);
4700   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4701                        MM_HEALTH(game_mm.laser_overload_value) :
4702                        game.health);
4703
4704   game.LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed : TimeLeft);
4705   game.LevelSolved_CountingScore = game.score_final;
4706   game.LevelSolved_CountingHealth = game.health_final;
4707 }
4708
4709 void GameWon(void)
4710 {
4711   static int time_count_steps;
4712   static int time, time_final;
4713   static int score, score_final;
4714   static int health, health_final;
4715   static int game_over_delay_1 = 0;
4716   static int game_over_delay_2 = 0;
4717   static int game_over_delay_3 = 0;
4718   int game_over_delay_value_1 = 50;
4719   int game_over_delay_value_2 = 25;
4720   int game_over_delay_value_3 = 50;
4721
4722   if (!game.LevelSolved_GameWon)
4723   {
4724     int i;
4725
4726     // do not start end game actions before the player stops moving (to exit)
4727     if (local_player->active && local_player->MovPos)
4728       return;
4729
4730     game.LevelSolved_GameWon = TRUE;
4731     game.LevelSolved_SaveTape = tape.recording;
4732     game.LevelSolved_SaveScore = !tape.playing;
4733
4734     if (!tape.playing)
4735     {
4736       LevelStats_incSolved(level_nr);
4737
4738       SaveLevelSetup_SeriesInfo();
4739     }
4740
4741     if (tape.auto_play)         // tape might already be stopped here
4742       tape.auto_play_level_solved = TRUE;
4743
4744     TapeStop();
4745
4746     game_over_delay_1 = 0;
4747     game_over_delay_2 = 0;
4748     game_over_delay_3 = game_over_delay_value_3;
4749
4750     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4751     score = score_final = game.score_final;
4752     health = health_final = game.health_final;
4753
4754     if (level.score[SC_TIME_BONUS] > 0)
4755     {
4756       if (TimeLeft > 0)
4757       {
4758         time_final = 0;
4759         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4760       }
4761       else if (game.no_time_limit && TimePlayed < 999)
4762       {
4763         time_final = 999;
4764         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4765       }
4766
4767       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4768
4769       game_over_delay_1 = game_over_delay_value_1;
4770
4771       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4772       {
4773         health_final = 0;
4774         score_final += health * level.score[SC_TIME_BONUS];
4775
4776         game_over_delay_2 = game_over_delay_value_2;
4777       }
4778
4779       game.score_final = score_final;
4780       game.health_final = health_final;
4781     }
4782
4783     if (level_editor_test_game)
4784     {
4785       time = time_final;
4786       score = score_final;
4787
4788       game.LevelSolved_CountingTime = time;
4789       game.LevelSolved_CountingScore = score;
4790
4791       game_panel_controls[GAME_PANEL_TIME].value = time;
4792       game_panel_controls[GAME_PANEL_SCORE].value = score;
4793
4794       DisplayGameControlValues();
4795     }
4796
4797     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4798     {
4799       // check if last player has left the level
4800       if (game.exit_x >= 0 &&
4801           game.exit_y >= 0)
4802       {
4803         int x = game.exit_x;
4804         int y = game.exit_y;
4805         int element = Tile[x][y];
4806
4807         // close exit door after last player
4808         if ((game.all_players_gone &&
4809              (element == EL_EXIT_OPEN ||
4810               element == EL_SP_EXIT_OPEN ||
4811               element == EL_STEEL_EXIT_OPEN)) ||
4812             element == EL_EM_EXIT_OPEN ||
4813             element == EL_EM_STEEL_EXIT_OPEN)
4814         {
4815
4816           Tile[x][y] =
4817             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4818              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4819              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4820              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4821              EL_EM_STEEL_EXIT_CLOSING);
4822
4823           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4824         }
4825
4826         // player disappears
4827         DrawLevelField(x, y);
4828       }
4829
4830       for (i = 0; i < MAX_PLAYERS; i++)
4831       {
4832         struct PlayerInfo *player = &stored_player[i];
4833
4834         if (player->present)
4835         {
4836           RemovePlayer(player);
4837
4838           // player disappears
4839           DrawLevelField(player->jx, player->jy);
4840         }
4841       }
4842     }
4843
4844     PlaySound(SND_GAME_WINNING);
4845   }
4846
4847   if (game_over_delay_1 > 0)
4848   {
4849     game_over_delay_1--;
4850
4851     return;
4852   }
4853
4854   if (time != time_final)
4855   {
4856     int time_to_go = ABS(time_final - time);
4857     int time_count_dir = (time < time_final ? +1 : -1);
4858
4859     if (time_to_go < time_count_steps)
4860       time_count_steps = 1;
4861
4862     time  += time_count_steps * time_count_dir;
4863     score += time_count_steps * level.score[SC_TIME_BONUS];
4864
4865     game.LevelSolved_CountingTime = time;
4866     game.LevelSolved_CountingScore = score;
4867
4868     game_panel_controls[GAME_PANEL_TIME].value = time;
4869     game_panel_controls[GAME_PANEL_SCORE].value = score;
4870
4871     DisplayGameControlValues();
4872
4873     if (time == time_final)
4874       StopSound(SND_GAME_LEVELTIME_BONUS);
4875     else if (setup.sound_loops)
4876       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4877     else
4878       PlaySound(SND_GAME_LEVELTIME_BONUS);
4879
4880     return;
4881   }
4882
4883   if (game_over_delay_2 > 0)
4884   {
4885     game_over_delay_2--;
4886
4887     return;
4888   }
4889
4890   if (health != health_final)
4891   {
4892     int health_count_dir = (health < health_final ? +1 : -1);
4893
4894     health += health_count_dir;
4895     score  += level.score[SC_TIME_BONUS];
4896
4897     game.LevelSolved_CountingHealth = health;
4898     game.LevelSolved_CountingScore = score;
4899
4900     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4901     game_panel_controls[GAME_PANEL_SCORE].value = score;
4902
4903     DisplayGameControlValues();
4904
4905     if (health == health_final)
4906       StopSound(SND_GAME_LEVELTIME_BONUS);
4907     else if (setup.sound_loops)
4908       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4909     else
4910       PlaySound(SND_GAME_LEVELTIME_BONUS);
4911
4912     return;
4913   }
4914
4915   game.panel.active = FALSE;
4916
4917   if (game_over_delay_3 > 0)
4918   {
4919     game_over_delay_3--;
4920
4921     return;
4922   }
4923
4924   GameEnd();
4925 }
4926
4927 void GameEnd(void)
4928 {
4929   // used instead of "level_nr" (needed for network games)
4930   int last_level_nr = levelset.level_nr;
4931   int hi_pos;
4932
4933   game.LevelSolved_GameEnd = TRUE;
4934
4935   if (game.LevelSolved_SaveTape)
4936   {
4937     // make sure that request dialog to save tape does not open door again
4938     if (!global.use_envelope_request)
4939       CloseDoor(DOOR_CLOSE_1);
4940
4941     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4942   }
4943
4944   // if no tape is to be saved, close both doors simultaneously
4945   CloseDoor(DOOR_CLOSE_ALL);
4946
4947   if (level_editor_test_game)
4948   {
4949     SetGameStatus(GAME_MODE_MAIN);
4950
4951     DrawMainMenu();
4952
4953     return;
4954   }
4955
4956   if (!game.LevelSolved_SaveScore)
4957   {
4958     SetGameStatus(GAME_MODE_MAIN);
4959
4960     DrawMainMenu();
4961
4962     return;
4963   }
4964
4965   if (level_nr == leveldir_current->handicap_level)
4966   {
4967     leveldir_current->handicap_level++;
4968
4969     SaveLevelSetup_SeriesInfo();
4970   }
4971
4972   if (setup.increment_levels &&
4973       level_nr < leveldir_current->last_level &&
4974       !network_playing)
4975   {
4976     level_nr++;         // advance to next level
4977     TapeErase();        // start with empty tape
4978
4979     if (setup.auto_play_next_level)
4980     {
4981       LoadLevel(level_nr);
4982
4983       SaveLevelSetup_SeriesInfo();
4984     }
4985   }
4986
4987   hi_pos = NewHiScore(last_level_nr);
4988
4989   if (hi_pos >= 0 && !setup.skip_scores_after_game)
4990   {
4991     SetGameStatus(GAME_MODE_SCORES);
4992
4993     DrawHallOfFame(last_level_nr, hi_pos);
4994   }
4995   else if (setup.auto_play_next_level && setup.increment_levels &&
4996            last_level_nr < leveldir_current->last_level &&
4997            !network_playing)
4998   {
4999     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5000   }
5001   else
5002   {
5003     SetGameStatus(GAME_MODE_MAIN);
5004
5005     DrawMainMenu();
5006   }
5007 }
5008
5009 int NewHiScore(int level_nr)
5010 {
5011   int k, l;
5012   int position = -1;
5013   boolean one_score_entry_per_name = !program.many_scores_per_name;
5014
5015   LoadScore(level_nr);
5016
5017   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5018       game.score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
5019     return -1;
5020
5021   for (k = 0; k < MAX_SCORE_ENTRIES; k++)
5022   {
5023     if (game.score_final > highscore[k].Score)
5024     {
5025       // player has made it to the hall of fame
5026
5027       if (k < MAX_SCORE_ENTRIES - 1)
5028       {
5029         int m = MAX_SCORE_ENTRIES - 1;
5030
5031         if (one_score_entry_per_name)
5032         {
5033           for (l = k; l < MAX_SCORE_ENTRIES; l++)
5034             if (strEqual(setup.player_name, highscore[l].Name))
5035               m = l;
5036
5037           if (m == k)   // player's new highscore overwrites his old one
5038             goto put_into_list;
5039         }
5040
5041         for (l = m; l > k; l--)
5042         {
5043           strcpy(highscore[l].Name, highscore[l - 1].Name);
5044           highscore[l].Score = highscore[l - 1].Score;
5045         }
5046       }
5047
5048       put_into_list:
5049
5050       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5051       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5052       highscore[k].Score = game.score_final;
5053       position = k;
5054
5055       break;
5056     }
5057     else if (one_score_entry_per_name &&
5058              !strncmp(setup.player_name, highscore[k].Name,
5059                       MAX_PLAYER_NAME_LEN))
5060       break;    // player already there with a higher score
5061   }
5062
5063   if (position >= 0) 
5064     SaveScore(level_nr);
5065
5066   return position;
5067 }
5068
5069 static int getElementMoveStepsizeExt(int x, int y, int direction)
5070 {
5071   int element = Tile[x][y];
5072   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5073   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5074   int horiz_move = (dx != 0);
5075   int sign = (horiz_move ? dx : dy);
5076   int step = sign * element_info[element].move_stepsize;
5077
5078   // special values for move stepsize for spring and things on conveyor belt
5079   if (horiz_move)
5080   {
5081     if (CAN_FALL(element) &&
5082         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5083       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5084     else if (element == EL_SPRING)
5085       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5086   }
5087
5088   return step;
5089 }
5090
5091 static int getElementMoveStepsize(int x, int y)
5092 {
5093   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5094 }
5095
5096 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5097 {
5098   if (player->GfxAction != action || player->GfxDir != dir)
5099   {
5100     player->GfxAction = action;
5101     player->GfxDir = dir;
5102     player->Frame = 0;
5103     player->StepFrame = 0;
5104   }
5105 }
5106
5107 static void ResetGfxFrame(int x, int y)
5108 {
5109   // profiling showed that "autotest" spends 10~20% of its time in this function
5110   if (DrawingDeactivatedField())
5111     return;
5112
5113   int element = Tile[x][y];
5114   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5115
5116   if (graphic_info[graphic].anim_global_sync)
5117     GfxFrame[x][y] = FrameCounter;
5118   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5119     GfxFrame[x][y] = CustomValue[x][y];
5120   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5121     GfxFrame[x][y] = element_info[element].collect_score;
5122   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5123     GfxFrame[x][y] = ChangeDelay[x][y];
5124 }
5125
5126 static void ResetGfxAnimation(int x, int y)
5127 {
5128   GfxAction[x][y] = ACTION_DEFAULT;
5129   GfxDir[x][y] = MovDir[x][y];
5130   GfxFrame[x][y] = 0;
5131
5132   ResetGfxFrame(x, y);
5133 }
5134
5135 static void ResetRandomAnimationValue(int x, int y)
5136 {
5137   GfxRandom[x][y] = INIT_GFX_RANDOM();
5138 }
5139
5140 static void InitMovingField(int x, int y, int direction)
5141 {
5142   int element = Tile[x][y];
5143   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5144   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5145   int newx = x + dx;
5146   int newy = y + dy;
5147   boolean is_moving_before, is_moving_after;
5148
5149   // check if element was/is moving or being moved before/after mode change
5150   is_moving_before = (WasJustMoving[x][y] != 0);
5151   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5152
5153   // reset animation only for moving elements which change direction of moving
5154   // or which just started or stopped moving
5155   // (else CEs with property "can move" / "not moving" are reset each frame)
5156   if (is_moving_before != is_moving_after ||
5157       direction != MovDir[x][y])
5158     ResetGfxAnimation(x, y);
5159
5160   MovDir[x][y] = direction;
5161   GfxDir[x][y] = direction;
5162
5163   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5164                      direction == MV_DOWN && CAN_FALL(element) ?
5165                      ACTION_FALLING : ACTION_MOVING);
5166
5167   // this is needed for CEs with property "can move" / "not moving"
5168
5169   if (is_moving_after)
5170   {
5171     if (Tile[newx][newy] == EL_EMPTY)
5172       Tile[newx][newy] = EL_BLOCKED;
5173
5174     MovDir[newx][newy] = MovDir[x][y];
5175
5176     CustomValue[newx][newy] = CustomValue[x][y];
5177
5178     GfxFrame[newx][newy] = GfxFrame[x][y];
5179     GfxRandom[newx][newy] = GfxRandom[x][y];
5180     GfxAction[newx][newy] = GfxAction[x][y];
5181     GfxDir[newx][newy] = GfxDir[x][y];
5182   }
5183 }
5184
5185 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5186 {
5187   int direction = MovDir[x][y];
5188   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5189   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5190
5191   *goes_to_x = newx;
5192   *goes_to_y = newy;
5193 }
5194
5195 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5196 {
5197   int oldx = x, oldy = y;
5198   int direction = MovDir[x][y];
5199
5200   if (direction == MV_LEFT)
5201     oldx++;
5202   else if (direction == MV_RIGHT)
5203     oldx--;
5204   else if (direction == MV_UP)
5205     oldy++;
5206   else if (direction == MV_DOWN)
5207     oldy--;
5208
5209   *comes_from_x = oldx;
5210   *comes_from_y = oldy;
5211 }
5212
5213 static int MovingOrBlocked2Element(int x, int y)
5214 {
5215   int element = Tile[x][y];
5216
5217   if (element == EL_BLOCKED)
5218   {
5219     int oldx, oldy;
5220
5221     Blocked2Moving(x, y, &oldx, &oldy);
5222     return Tile[oldx][oldy];
5223   }
5224   else
5225     return element;
5226 }
5227
5228 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5229 {
5230   // like MovingOrBlocked2Element(), but if element is moving
5231   // and (x,y) is the field the moving element is just leaving,
5232   // return EL_BLOCKED instead of the element value
5233   int element = Tile[x][y];
5234
5235   if (IS_MOVING(x, y))
5236   {
5237     if (element == EL_BLOCKED)
5238     {
5239       int oldx, oldy;
5240
5241       Blocked2Moving(x, y, &oldx, &oldy);
5242       return Tile[oldx][oldy];
5243     }
5244     else
5245       return EL_BLOCKED;
5246   }
5247   else
5248     return element;
5249 }
5250
5251 static void RemoveField(int x, int y)
5252 {
5253   Tile[x][y] = EL_EMPTY;
5254
5255   MovPos[x][y] = 0;
5256   MovDir[x][y] = 0;
5257   MovDelay[x][y] = 0;
5258
5259   CustomValue[x][y] = 0;
5260
5261   AmoebaNr[x][y] = 0;
5262   ChangeDelay[x][y] = 0;
5263   ChangePage[x][y] = -1;
5264   Pushed[x][y] = FALSE;
5265
5266   GfxElement[x][y] = EL_UNDEFINED;
5267   GfxAction[x][y] = ACTION_DEFAULT;
5268   GfxDir[x][y] = MV_NONE;
5269 }
5270
5271 static void RemoveMovingField(int x, int y)
5272 {
5273   int oldx = x, oldy = y, newx = x, newy = y;
5274   int element = Tile[x][y];
5275   int next_element = EL_UNDEFINED;
5276
5277   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5278     return;
5279
5280   if (IS_MOVING(x, y))
5281   {
5282     Moving2Blocked(x, y, &newx, &newy);
5283
5284     if (Tile[newx][newy] != EL_BLOCKED)
5285     {
5286       // element is moving, but target field is not free (blocked), but
5287       // already occupied by something different (example: acid pool);
5288       // in this case, only remove the moving field, but not the target
5289
5290       RemoveField(oldx, oldy);
5291
5292       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5293
5294       TEST_DrawLevelField(oldx, oldy);
5295
5296       return;
5297     }
5298   }
5299   else if (element == EL_BLOCKED)
5300   {
5301     Blocked2Moving(x, y, &oldx, &oldy);
5302     if (!IS_MOVING(oldx, oldy))
5303       return;
5304   }
5305
5306   if (element == EL_BLOCKED &&
5307       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5308        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5309        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5310        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5311        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5312        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5313     next_element = get_next_element(Tile[oldx][oldy]);
5314
5315   RemoveField(oldx, oldy);
5316   RemoveField(newx, newy);
5317
5318   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5319
5320   if (next_element != EL_UNDEFINED)
5321     Tile[oldx][oldy] = next_element;
5322
5323   TEST_DrawLevelField(oldx, oldy);
5324   TEST_DrawLevelField(newx, newy);
5325 }
5326
5327 void DrawDynamite(int x, int y)
5328 {
5329   int sx = SCREENX(x), sy = SCREENY(y);
5330   int graphic = el2img(Tile[x][y]);
5331   int frame;
5332
5333   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5334     return;
5335
5336   if (IS_WALKABLE_INSIDE(Back[x][y]))
5337     return;
5338
5339   if (Back[x][y])
5340     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5341   else if (Store[x][y])
5342     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5343
5344   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5345
5346   if (Back[x][y] || Store[x][y])
5347     DrawGraphicThruMask(sx, sy, graphic, frame);
5348   else
5349     DrawGraphic(sx, sy, graphic, frame);
5350 }
5351
5352 static void CheckDynamite(int x, int y)
5353 {
5354   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5355   {
5356     MovDelay[x][y]--;
5357
5358     if (MovDelay[x][y] != 0)
5359     {
5360       DrawDynamite(x, y);
5361       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5362
5363       return;
5364     }
5365   }
5366
5367   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5368
5369   Bang(x, y);
5370 }
5371
5372 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5373 {
5374   boolean num_checked_players = 0;
5375   int i;
5376
5377   for (i = 0; i < MAX_PLAYERS; i++)
5378   {
5379     if (stored_player[i].active)
5380     {
5381       int sx = stored_player[i].jx;
5382       int sy = stored_player[i].jy;
5383
5384       if (num_checked_players == 0)
5385       {
5386         *sx1 = *sx2 = sx;
5387         *sy1 = *sy2 = sy;
5388       }
5389       else
5390       {
5391         *sx1 = MIN(*sx1, sx);
5392         *sy1 = MIN(*sy1, sy);
5393         *sx2 = MAX(*sx2, sx);
5394         *sy2 = MAX(*sy2, sy);
5395       }
5396
5397       num_checked_players++;
5398     }
5399   }
5400 }
5401
5402 static boolean checkIfAllPlayersFitToScreen_RND(void)
5403 {
5404   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5405
5406   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5407
5408   return (sx2 - sx1 < SCR_FIELDX &&
5409           sy2 - sy1 < SCR_FIELDY);
5410 }
5411
5412 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5413 {
5414   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5415
5416   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5417
5418   *sx = (sx1 + sx2) / 2;
5419   *sy = (sy1 + sy2) / 2;
5420 }
5421
5422 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5423                                boolean center_screen, boolean quick_relocation)
5424 {
5425   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5426   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5427   boolean no_delay = (tape.warp_forward);
5428   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5429   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5430   int new_scroll_x, new_scroll_y;
5431
5432   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5433   {
5434     // case 1: quick relocation inside visible screen (without scrolling)
5435
5436     RedrawPlayfield();
5437
5438     return;
5439   }
5440
5441   if (!level.shifted_relocation || center_screen)
5442   {
5443     // relocation _with_ centering of screen
5444
5445     new_scroll_x = SCROLL_POSITION_X(x);
5446     new_scroll_y = SCROLL_POSITION_Y(y);
5447   }
5448   else
5449   {
5450     // relocation _without_ centering of screen
5451
5452     int center_scroll_x = SCROLL_POSITION_X(old_x);
5453     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5454     int offset_x = x + (scroll_x - center_scroll_x);
5455     int offset_y = y + (scroll_y - center_scroll_y);
5456
5457     // for new screen position, apply previous offset to center position
5458     new_scroll_x = SCROLL_POSITION_X(offset_x);
5459     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5460   }
5461
5462   if (quick_relocation)
5463   {
5464     // case 2: quick relocation (redraw without visible scrolling)
5465
5466     scroll_x = new_scroll_x;
5467     scroll_y = new_scroll_y;
5468
5469     RedrawPlayfield();
5470
5471     return;
5472   }
5473
5474   // case 3: visible relocation (with scrolling to new position)
5475
5476   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5477
5478   SetVideoFrameDelay(wait_delay_value);
5479
5480   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5481   {
5482     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5483     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5484
5485     if (dx == 0 && dy == 0)             // no scrolling needed at all
5486       break;
5487
5488     scroll_x -= dx;
5489     scroll_y -= dy;
5490
5491     // set values for horizontal/vertical screen scrolling (half tile size)
5492     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5493     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5494     int pos_x = dx * TILEX / 2;
5495     int pos_y = dy * TILEY / 2;
5496     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5497     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5498
5499     ScrollLevel(dx, dy);
5500     DrawAllPlayers();
5501
5502     // scroll in two steps of half tile size to make things smoother
5503     BlitScreenToBitmapExt_RND(window, fx, fy);
5504
5505     // scroll second step to align at full tile size
5506     BlitScreenToBitmap(window);
5507   }
5508
5509   DrawAllPlayers();
5510   BackToFront();
5511
5512   SetVideoFrameDelay(frame_delay_value_old);
5513 }
5514
5515 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5516 {
5517   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5518   int player_nr = GET_PLAYER_NR(el_player);
5519   struct PlayerInfo *player = &stored_player[player_nr];
5520   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5521   boolean no_delay = (tape.warp_forward);
5522   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5523   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5524   int old_jx = player->jx;
5525   int old_jy = player->jy;
5526   int old_element = Tile[old_jx][old_jy];
5527   int element = Tile[jx][jy];
5528   boolean player_relocated = (old_jx != jx || old_jy != jy);
5529
5530   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5531   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5532   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5533   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5534   int leave_side_horiz = move_dir_horiz;
5535   int leave_side_vert  = move_dir_vert;
5536   int enter_side = enter_side_horiz | enter_side_vert;
5537   int leave_side = leave_side_horiz | leave_side_vert;
5538
5539   if (player->buried)           // do not reanimate dead player
5540     return;
5541
5542   if (!player_relocated)        // no need to relocate the player
5543     return;
5544
5545   if (IS_PLAYER(jx, jy))        // player already placed at new position
5546   {
5547     RemoveField(jx, jy);        // temporarily remove newly placed player
5548     DrawLevelField(jx, jy);
5549   }
5550
5551   if (player->present)
5552   {
5553     while (player->MovPos)
5554     {
5555       ScrollPlayer(player, SCROLL_GO_ON);
5556       ScrollScreen(NULL, SCROLL_GO_ON);
5557
5558       AdvanceFrameAndPlayerCounters(player->index_nr);
5559
5560       DrawPlayer(player);
5561
5562       BackToFront_WithFrameDelay(wait_delay_value);
5563     }
5564
5565     DrawPlayer(player);         // needed here only to cleanup last field
5566     DrawLevelField(player->jx, player->jy);     // remove player graphic
5567
5568     player->is_moving = FALSE;
5569   }
5570
5571   if (IS_CUSTOM_ELEMENT(old_element))
5572     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5573                                CE_LEFT_BY_PLAYER,
5574                                player->index_bit, leave_side);
5575
5576   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5577                                       CE_PLAYER_LEAVES_X,
5578                                       player->index_bit, leave_side);
5579
5580   Tile[jx][jy] = el_player;
5581   InitPlayerField(jx, jy, el_player, TRUE);
5582
5583   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5584      possible that the relocation target field did not contain a player element,
5585      but a walkable element, to which the new player was relocated -- in this
5586      case, restore that (already initialized!) element on the player field */
5587   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5588   {
5589     Tile[jx][jy] = element;     // restore previously existing element
5590   }
5591
5592   // only visually relocate centered player
5593   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5594                      FALSE, level.instant_relocation);
5595
5596   TestIfPlayerTouchesBadThing(jx, jy);
5597   TestIfPlayerTouchesCustomElement(jx, jy);
5598
5599   if (IS_CUSTOM_ELEMENT(element))
5600     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5601                                player->index_bit, enter_side);
5602
5603   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5604                                       player->index_bit, enter_side);
5605
5606   if (player->is_switching)
5607   {
5608     /* ensure that relocation while still switching an element does not cause
5609        a new element to be treated as also switched directly after relocation
5610        (this is important for teleporter switches that teleport the player to
5611        a place where another teleporter switch is in the same direction, which
5612        would then incorrectly be treated as immediately switched before the
5613        direction key that caused the switch was released) */
5614
5615     player->switch_x += jx - old_jx;
5616     player->switch_y += jy - old_jy;
5617   }
5618 }
5619
5620 static void Explode(int ex, int ey, int phase, int mode)
5621 {
5622   int x, y;
5623   int last_phase;
5624   int border_element;
5625
5626   // !!! eliminate this variable !!!
5627   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5628
5629   if (game.explosions_delayed)
5630   {
5631     ExplodeField[ex][ey] = mode;
5632     return;
5633   }
5634
5635   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5636   {
5637     int center_element = Tile[ex][ey];
5638     int artwork_element, explosion_element;     // set these values later
5639
5640     // remove things displayed in background while burning dynamite
5641     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5642       Back[ex][ey] = 0;
5643
5644     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5645     {
5646       // put moving element to center field (and let it explode there)
5647       center_element = MovingOrBlocked2Element(ex, ey);
5648       RemoveMovingField(ex, ey);
5649       Tile[ex][ey] = center_element;
5650     }
5651
5652     // now "center_element" is finally determined -- set related values now
5653     artwork_element = center_element;           // for custom player artwork
5654     explosion_element = center_element;         // for custom player artwork
5655
5656     if (IS_PLAYER(ex, ey))
5657     {
5658       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5659
5660       artwork_element = stored_player[player_nr].artwork_element;
5661
5662       if (level.use_explosion_element[player_nr])
5663       {
5664         explosion_element = level.explosion_element[player_nr];
5665         artwork_element = explosion_element;
5666       }
5667     }
5668
5669     if (mode == EX_TYPE_NORMAL ||
5670         mode == EX_TYPE_CENTER ||
5671         mode == EX_TYPE_CROSS)
5672       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5673
5674     last_phase = element_info[explosion_element].explosion_delay + 1;
5675
5676     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5677     {
5678       int xx = x - ex + 1;
5679       int yy = y - ey + 1;
5680       int element;
5681
5682       if (!IN_LEV_FIELD(x, y) ||
5683           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5684           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5685         continue;
5686
5687       element = Tile[x][y];
5688
5689       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5690       {
5691         element = MovingOrBlocked2Element(x, y);
5692
5693         if (!IS_EXPLOSION_PROOF(element))
5694           RemoveMovingField(x, y);
5695       }
5696
5697       // indestructible elements can only explode in center (but not flames)
5698       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5699                                            mode == EX_TYPE_BORDER)) ||
5700           element == EL_FLAMES)
5701         continue;
5702
5703       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5704          behaviour, for example when touching a yamyam that explodes to rocks
5705          with active deadly shield, a rock is created under the player !!! */
5706       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5707 #if 0
5708       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5709           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5710            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5711 #else
5712       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5713 #endif
5714       {
5715         if (IS_ACTIVE_BOMB(element))
5716         {
5717           // re-activate things under the bomb like gate or penguin
5718           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5719           Back[x][y] = 0;
5720         }
5721
5722         continue;
5723       }
5724
5725       // save walkable background elements while explosion on same tile
5726       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5727           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5728         Back[x][y] = element;
5729
5730       // ignite explodable elements reached by other explosion
5731       if (element == EL_EXPLOSION)
5732         element = Store2[x][y];
5733
5734       if (AmoebaNr[x][y] &&
5735           (element == EL_AMOEBA_FULL ||
5736            element == EL_BD_AMOEBA ||
5737            element == EL_AMOEBA_GROWING))
5738       {
5739         AmoebaCnt[AmoebaNr[x][y]]--;
5740         AmoebaCnt2[AmoebaNr[x][y]]--;
5741       }
5742
5743       RemoveField(x, y);
5744
5745       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5746       {
5747         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5748
5749         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5750
5751         if (PLAYERINFO(ex, ey)->use_murphy)
5752           Store[x][y] = EL_EMPTY;
5753       }
5754
5755       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5756       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5757       else if (ELEM_IS_PLAYER(center_element))
5758         Store[x][y] = EL_EMPTY;
5759       else if (center_element == EL_YAMYAM)
5760         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5761       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5762         Store[x][y] = element_info[center_element].content.e[xx][yy];
5763 #if 1
5764       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5765       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5766       // otherwise) -- FIX THIS !!!
5767       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5768         Store[x][y] = element_info[element].content.e[1][1];
5769 #else
5770       else if (!CAN_EXPLODE(element))
5771         Store[x][y] = element_info[element].content.e[1][1];
5772 #endif
5773       else
5774         Store[x][y] = EL_EMPTY;
5775
5776       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5777           center_element == EL_AMOEBA_TO_DIAMOND)
5778         Store2[x][y] = element;
5779
5780       Tile[x][y] = EL_EXPLOSION;
5781       GfxElement[x][y] = artwork_element;
5782
5783       ExplodePhase[x][y] = 1;
5784       ExplodeDelay[x][y] = last_phase;
5785
5786       Stop[x][y] = TRUE;
5787     }
5788
5789     if (center_element == EL_YAMYAM)
5790       game.yamyam_content_nr =
5791         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5792
5793     return;
5794   }
5795
5796   if (Stop[ex][ey])
5797     return;
5798
5799   x = ex;
5800   y = ey;
5801
5802   if (phase == 1)
5803     GfxFrame[x][y] = 0;         // restart explosion animation
5804
5805   last_phase = ExplodeDelay[x][y];
5806
5807   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5808
5809   // this can happen if the player leaves an explosion just in time
5810   if (GfxElement[x][y] == EL_UNDEFINED)
5811     GfxElement[x][y] = EL_EMPTY;
5812
5813   border_element = Store2[x][y];
5814   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5815     border_element = StorePlayer[x][y];
5816
5817   if (phase == element_info[border_element].ignition_delay ||
5818       phase == last_phase)
5819   {
5820     boolean border_explosion = FALSE;
5821
5822     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5823         !PLAYER_EXPLOSION_PROTECTED(x, y))
5824     {
5825       KillPlayerUnlessExplosionProtected(x, y);
5826       border_explosion = TRUE;
5827     }
5828     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5829     {
5830       Tile[x][y] = Store2[x][y];
5831       Store2[x][y] = 0;
5832       Bang(x, y);
5833       border_explosion = TRUE;
5834     }
5835     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5836     {
5837       AmoebaToDiamond(x, y);
5838       Store2[x][y] = 0;
5839       border_explosion = TRUE;
5840     }
5841
5842     // if an element just explodes due to another explosion (chain-reaction),
5843     // do not immediately end the new explosion when it was the last frame of
5844     // the explosion (as it would be done in the following "if"-statement!)
5845     if (border_explosion && phase == last_phase)
5846       return;
5847   }
5848
5849   if (phase == last_phase)
5850   {
5851     int element;
5852
5853     element = Tile[x][y] = Store[x][y];
5854     Store[x][y] = Store2[x][y] = 0;
5855     GfxElement[x][y] = EL_UNDEFINED;
5856
5857     // player can escape from explosions and might therefore be still alive
5858     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5859         element <= EL_PLAYER_IS_EXPLODING_4)
5860     {
5861       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5862       int explosion_element = EL_PLAYER_1 + player_nr;
5863       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5864       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5865
5866       if (level.use_explosion_element[player_nr])
5867         explosion_element = level.explosion_element[player_nr];
5868
5869       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5870                     element_info[explosion_element].content.e[xx][yy]);
5871     }
5872
5873     // restore probably existing indestructible background element
5874     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5875       element = Tile[x][y] = Back[x][y];
5876     Back[x][y] = 0;
5877
5878     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5879     GfxDir[x][y] = MV_NONE;
5880     ChangeDelay[x][y] = 0;
5881     ChangePage[x][y] = -1;
5882
5883     CustomValue[x][y] = 0;
5884
5885     InitField_WithBug2(x, y, FALSE);
5886
5887     TEST_DrawLevelField(x, y);
5888
5889     TestIfElementTouchesCustomElement(x, y);
5890
5891     if (GFX_CRUMBLED(element))
5892       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5893
5894     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5895       StorePlayer[x][y] = 0;
5896
5897     if (ELEM_IS_PLAYER(element))
5898       RelocatePlayer(x, y, element);
5899   }
5900   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5901   {
5902     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5903     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5904
5905     if (phase == delay)
5906       TEST_DrawLevelFieldCrumbled(x, y);
5907
5908     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5909     {
5910       DrawLevelElement(x, y, Back[x][y]);
5911       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5912     }
5913     else if (IS_WALKABLE_UNDER(Back[x][y]))
5914     {
5915       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5916       DrawLevelElementThruMask(x, y, Back[x][y]);
5917     }
5918     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5919       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5920   }
5921 }
5922
5923 static void DynaExplode(int ex, int ey)
5924 {
5925   int i, j;
5926   int dynabomb_element = Tile[ex][ey];
5927   int dynabomb_size = 1;
5928   boolean dynabomb_xl = FALSE;
5929   struct PlayerInfo *player;
5930   static int xy[4][2] =
5931   {
5932     { 0, -1 },
5933     { -1, 0 },
5934     { +1, 0 },
5935     { 0, +1 }
5936   };
5937
5938   if (IS_ACTIVE_BOMB(dynabomb_element))
5939   {
5940     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5941     dynabomb_size = player->dynabomb_size;
5942     dynabomb_xl = player->dynabomb_xl;
5943     player->dynabombs_left++;
5944   }
5945
5946   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5947
5948   for (i = 0; i < NUM_DIRECTIONS; i++)
5949   {
5950     for (j = 1; j <= dynabomb_size; j++)
5951     {
5952       int x = ex + j * xy[i][0];
5953       int y = ey + j * xy[i][1];
5954       int element;
5955
5956       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
5957         break;
5958
5959       element = Tile[x][y];
5960
5961       // do not restart explosions of fields with active bombs
5962       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5963         continue;
5964
5965       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5966
5967       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5968           !IS_DIGGABLE(element) && !dynabomb_xl)
5969         break;
5970     }
5971   }
5972 }
5973
5974 void Bang(int x, int y)
5975 {
5976   int element = MovingOrBlocked2Element(x, y);
5977   int explosion_type = EX_TYPE_NORMAL;
5978
5979   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5980   {
5981     struct PlayerInfo *player = PLAYERINFO(x, y);
5982
5983     element = Tile[x][y] = player->initial_element;
5984
5985     if (level.use_explosion_element[player->index_nr])
5986     {
5987       int explosion_element = level.explosion_element[player->index_nr];
5988
5989       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5990         explosion_type = EX_TYPE_CROSS;
5991       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5992         explosion_type = EX_TYPE_CENTER;
5993     }
5994   }
5995
5996   switch (element)
5997   {
5998     case EL_BUG:
5999     case EL_SPACESHIP:
6000     case EL_BD_BUTTERFLY:
6001     case EL_BD_FIREFLY:
6002     case EL_YAMYAM:
6003     case EL_DARK_YAMYAM:
6004     case EL_ROBOT:
6005     case EL_PACMAN:
6006     case EL_MOLE:
6007       RaiseScoreElement(element);
6008       break;
6009
6010     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6011     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6012     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6013     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6014     case EL_DYNABOMB_INCREASE_NUMBER:
6015     case EL_DYNABOMB_INCREASE_SIZE:
6016     case EL_DYNABOMB_INCREASE_POWER:
6017       explosion_type = EX_TYPE_DYNA;
6018       break;
6019
6020     case EL_DC_LANDMINE:
6021       explosion_type = EX_TYPE_CENTER;
6022       break;
6023
6024     case EL_PENGUIN:
6025     case EL_LAMP:
6026     case EL_LAMP_ACTIVE:
6027     case EL_AMOEBA_TO_DIAMOND:
6028       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6029         explosion_type = EX_TYPE_CENTER;
6030       break;
6031
6032     default:
6033       if (element_info[element].explosion_type == EXPLODES_CROSS)
6034         explosion_type = EX_TYPE_CROSS;
6035       else if (element_info[element].explosion_type == EXPLODES_1X1)
6036         explosion_type = EX_TYPE_CENTER;
6037       break;
6038   }
6039
6040   if (explosion_type == EX_TYPE_DYNA)
6041     DynaExplode(x, y);
6042   else
6043     Explode(x, y, EX_PHASE_START, explosion_type);
6044
6045   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6046 }
6047
6048 static void SplashAcid(int x, int y)
6049 {
6050   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6051       (!IN_LEV_FIELD(x - 1, y - 2) ||
6052        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6053     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6054
6055   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6056       (!IN_LEV_FIELD(x + 1, y - 2) ||
6057        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6058     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6059
6060   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6061 }
6062
6063 static void InitBeltMovement(void)
6064 {
6065   static int belt_base_element[4] =
6066   {
6067     EL_CONVEYOR_BELT_1_LEFT,
6068     EL_CONVEYOR_BELT_2_LEFT,
6069     EL_CONVEYOR_BELT_3_LEFT,
6070     EL_CONVEYOR_BELT_4_LEFT
6071   };
6072   static int belt_base_active_element[4] =
6073   {
6074     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6075     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6076     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6077     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6078   };
6079
6080   int x, y, i, j;
6081
6082   // set frame order for belt animation graphic according to belt direction
6083   for (i = 0; i < NUM_BELTS; i++)
6084   {
6085     int belt_nr = i;
6086
6087     for (j = 0; j < NUM_BELT_PARTS; j++)
6088     {
6089       int element = belt_base_active_element[belt_nr] + j;
6090       int graphic_1 = el2img(element);
6091       int graphic_2 = el2panelimg(element);
6092
6093       if (game.belt_dir[i] == MV_LEFT)
6094       {
6095         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6096         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6097       }
6098       else
6099       {
6100         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6101         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6102       }
6103     }
6104   }
6105
6106   SCAN_PLAYFIELD(x, y)
6107   {
6108     int element = Tile[x][y];
6109
6110     for (i = 0; i < NUM_BELTS; i++)
6111     {
6112       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6113       {
6114         int e_belt_nr = getBeltNrFromBeltElement(element);
6115         int belt_nr = i;
6116
6117         if (e_belt_nr == belt_nr)
6118         {
6119           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6120
6121           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6122         }
6123       }
6124     }
6125   }
6126 }
6127
6128 static void ToggleBeltSwitch(int x, int y)
6129 {
6130   static int belt_base_element[4] =
6131   {
6132     EL_CONVEYOR_BELT_1_LEFT,
6133     EL_CONVEYOR_BELT_2_LEFT,
6134     EL_CONVEYOR_BELT_3_LEFT,
6135     EL_CONVEYOR_BELT_4_LEFT
6136   };
6137   static int belt_base_active_element[4] =
6138   {
6139     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6140     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6141     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6142     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6143   };
6144   static int belt_base_switch_element[4] =
6145   {
6146     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6147     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6148     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6149     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6150   };
6151   static int belt_move_dir[4] =
6152   {
6153     MV_LEFT,
6154     MV_NONE,
6155     MV_RIGHT,
6156     MV_NONE,
6157   };
6158
6159   int element = Tile[x][y];
6160   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6161   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6162   int belt_dir = belt_move_dir[belt_dir_nr];
6163   int xx, yy, i;
6164
6165   if (!IS_BELT_SWITCH(element))
6166     return;
6167
6168   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6169   game.belt_dir[belt_nr] = belt_dir;
6170
6171   if (belt_dir_nr == 3)
6172     belt_dir_nr = 1;
6173
6174   // set frame order for belt animation graphic according to belt direction
6175   for (i = 0; i < NUM_BELT_PARTS; i++)
6176   {
6177     int element = belt_base_active_element[belt_nr] + i;
6178     int graphic_1 = el2img(element);
6179     int graphic_2 = el2panelimg(element);
6180
6181     if (belt_dir == MV_LEFT)
6182     {
6183       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6184       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6185     }
6186     else
6187     {
6188       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6189       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6190     }
6191   }
6192
6193   SCAN_PLAYFIELD(xx, yy)
6194   {
6195     int element = Tile[xx][yy];
6196
6197     if (IS_BELT_SWITCH(element))
6198     {
6199       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6200
6201       if (e_belt_nr == belt_nr)
6202       {
6203         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6204         TEST_DrawLevelField(xx, yy);
6205       }
6206     }
6207     else if (IS_BELT(element) && belt_dir != MV_NONE)
6208     {
6209       int e_belt_nr = getBeltNrFromBeltElement(element);
6210
6211       if (e_belt_nr == belt_nr)
6212       {
6213         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6214
6215         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6216         TEST_DrawLevelField(xx, yy);
6217       }
6218     }
6219     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6220     {
6221       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6222
6223       if (e_belt_nr == belt_nr)
6224       {
6225         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6226
6227         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6228         TEST_DrawLevelField(xx, yy);
6229       }
6230     }
6231   }
6232 }
6233
6234 static void ToggleSwitchgateSwitch(int x, int y)
6235 {
6236   int xx, yy;
6237
6238   game.switchgate_pos = !game.switchgate_pos;
6239
6240   SCAN_PLAYFIELD(xx, yy)
6241   {
6242     int element = Tile[xx][yy];
6243
6244     if (element == EL_SWITCHGATE_SWITCH_UP)
6245     {
6246       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6247       TEST_DrawLevelField(xx, yy);
6248     }
6249     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6250     {
6251       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6252       TEST_DrawLevelField(xx, yy);
6253     }
6254     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6255     {
6256       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6257       TEST_DrawLevelField(xx, yy);
6258     }
6259     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6260     {
6261       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6262       TEST_DrawLevelField(xx, yy);
6263     }
6264     else if (element == EL_SWITCHGATE_OPEN ||
6265              element == EL_SWITCHGATE_OPENING)
6266     {
6267       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6268
6269       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6270     }
6271     else if (element == EL_SWITCHGATE_CLOSED ||
6272              element == EL_SWITCHGATE_CLOSING)
6273     {
6274       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6275
6276       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6277     }
6278   }
6279 }
6280
6281 static int getInvisibleActiveFromInvisibleElement(int element)
6282 {
6283   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6284           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6285           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6286           element);
6287 }
6288
6289 static int getInvisibleFromInvisibleActiveElement(int element)
6290 {
6291   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6292           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6293           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6294           element);
6295 }
6296
6297 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6298 {
6299   int x, y;
6300
6301   SCAN_PLAYFIELD(x, y)
6302   {
6303     int element = Tile[x][y];
6304
6305     if (element == EL_LIGHT_SWITCH &&
6306         game.light_time_left > 0)
6307     {
6308       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6309       TEST_DrawLevelField(x, y);
6310     }
6311     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6312              game.light_time_left == 0)
6313     {
6314       Tile[x][y] = EL_LIGHT_SWITCH;
6315       TEST_DrawLevelField(x, y);
6316     }
6317     else if (element == EL_EMC_DRIPPER &&
6318              game.light_time_left > 0)
6319     {
6320       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6321       TEST_DrawLevelField(x, y);
6322     }
6323     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6324              game.light_time_left == 0)
6325     {
6326       Tile[x][y] = EL_EMC_DRIPPER;
6327       TEST_DrawLevelField(x, y);
6328     }
6329     else if (element == EL_INVISIBLE_STEELWALL ||
6330              element == EL_INVISIBLE_WALL ||
6331              element == EL_INVISIBLE_SAND)
6332     {
6333       if (game.light_time_left > 0)
6334         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6335
6336       TEST_DrawLevelField(x, y);
6337
6338       // uncrumble neighbour fields, if needed
6339       if (element == EL_INVISIBLE_SAND)
6340         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6341     }
6342     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6343              element == EL_INVISIBLE_WALL_ACTIVE ||
6344              element == EL_INVISIBLE_SAND_ACTIVE)
6345     {
6346       if (game.light_time_left == 0)
6347         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6348
6349       TEST_DrawLevelField(x, y);
6350
6351       // re-crumble neighbour fields, if needed
6352       if (element == EL_INVISIBLE_SAND)
6353         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6354     }
6355   }
6356 }
6357
6358 static void RedrawAllInvisibleElementsForLenses(void)
6359 {
6360   int x, y;
6361
6362   SCAN_PLAYFIELD(x, y)
6363   {
6364     int element = Tile[x][y];
6365
6366     if (element == EL_EMC_DRIPPER &&
6367         game.lenses_time_left > 0)
6368     {
6369       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6370       TEST_DrawLevelField(x, y);
6371     }
6372     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6373              game.lenses_time_left == 0)
6374     {
6375       Tile[x][y] = EL_EMC_DRIPPER;
6376       TEST_DrawLevelField(x, y);
6377     }
6378     else if (element == EL_INVISIBLE_STEELWALL ||
6379              element == EL_INVISIBLE_WALL ||
6380              element == EL_INVISIBLE_SAND)
6381     {
6382       if (game.lenses_time_left > 0)
6383         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6384
6385       TEST_DrawLevelField(x, y);
6386
6387       // uncrumble neighbour fields, if needed
6388       if (element == EL_INVISIBLE_SAND)
6389         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6390     }
6391     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6392              element == EL_INVISIBLE_WALL_ACTIVE ||
6393              element == EL_INVISIBLE_SAND_ACTIVE)
6394     {
6395       if (game.lenses_time_left == 0)
6396         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6397
6398       TEST_DrawLevelField(x, y);
6399
6400       // re-crumble neighbour fields, if needed
6401       if (element == EL_INVISIBLE_SAND)
6402         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6403     }
6404   }
6405 }
6406
6407 static void RedrawAllInvisibleElementsForMagnifier(void)
6408 {
6409   int x, y;
6410
6411   SCAN_PLAYFIELD(x, y)
6412   {
6413     int element = Tile[x][y];
6414
6415     if (element == EL_EMC_FAKE_GRASS &&
6416         game.magnify_time_left > 0)
6417     {
6418       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6419       TEST_DrawLevelField(x, y);
6420     }
6421     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6422              game.magnify_time_left == 0)
6423     {
6424       Tile[x][y] = EL_EMC_FAKE_GRASS;
6425       TEST_DrawLevelField(x, y);
6426     }
6427     else if (IS_GATE_GRAY(element) &&
6428              game.magnify_time_left > 0)
6429     {
6430       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6431                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6432                     IS_EM_GATE_GRAY(element) ?
6433                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6434                     IS_EMC_GATE_GRAY(element) ?
6435                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6436                     IS_DC_GATE_GRAY(element) ?
6437                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6438                     element);
6439       TEST_DrawLevelField(x, y);
6440     }
6441     else if (IS_GATE_GRAY_ACTIVE(element) &&
6442              game.magnify_time_left == 0)
6443     {
6444       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6445                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6446                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6447                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6448                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6449                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6450                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6451                     EL_DC_GATE_WHITE_GRAY :
6452                     element);
6453       TEST_DrawLevelField(x, y);
6454     }
6455   }
6456 }
6457
6458 static void ToggleLightSwitch(int x, int y)
6459 {
6460   int element = Tile[x][y];
6461
6462   game.light_time_left =
6463     (element == EL_LIGHT_SWITCH ?
6464      level.time_light * FRAMES_PER_SECOND : 0);
6465
6466   RedrawAllLightSwitchesAndInvisibleElements();
6467 }
6468
6469 static void ActivateTimegateSwitch(int x, int y)
6470 {
6471   int xx, yy;
6472
6473   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6474
6475   SCAN_PLAYFIELD(xx, yy)
6476   {
6477     int element = Tile[xx][yy];
6478
6479     if (element == EL_TIMEGATE_CLOSED ||
6480         element == EL_TIMEGATE_CLOSING)
6481     {
6482       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6483       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6484     }
6485
6486     /*
6487     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6488     {
6489       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6490       TEST_DrawLevelField(xx, yy);
6491     }
6492     */
6493
6494   }
6495
6496   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6497                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6498 }
6499
6500 static void Impact(int x, int y)
6501 {
6502   boolean last_line = (y == lev_fieldy - 1);
6503   boolean object_hit = FALSE;
6504   boolean impact = (last_line || object_hit);
6505   int element = Tile[x][y];
6506   int smashed = EL_STEELWALL;
6507
6508   if (!last_line)       // check if element below was hit
6509   {
6510     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6511       return;
6512
6513     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6514                                          MovDir[x][y + 1] != MV_DOWN ||
6515                                          MovPos[x][y + 1] <= TILEY / 2));
6516
6517     // do not smash moving elements that left the smashed field in time
6518     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6519         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6520       object_hit = FALSE;
6521
6522 #if USE_QUICKSAND_IMPACT_BUGFIX
6523     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6524     {
6525       RemoveMovingField(x, y + 1);
6526       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6527       Tile[x][y + 2] = EL_ROCK;
6528       TEST_DrawLevelField(x, y + 2);
6529
6530       object_hit = TRUE;
6531     }
6532
6533     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6534     {
6535       RemoveMovingField(x, y + 1);
6536       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6537       Tile[x][y + 2] = EL_ROCK;
6538       TEST_DrawLevelField(x, y + 2);
6539
6540       object_hit = TRUE;
6541     }
6542 #endif
6543
6544     if (object_hit)
6545       smashed = MovingOrBlocked2Element(x, y + 1);
6546
6547     impact = (last_line || object_hit);
6548   }
6549
6550   if (!last_line && smashed == EL_ACID) // element falls into acid
6551   {
6552     SplashAcid(x, y + 1);
6553     return;
6554   }
6555
6556   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6557   // only reset graphic animation if graphic really changes after impact
6558   if (impact &&
6559       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6560   {
6561     ResetGfxAnimation(x, y);
6562     TEST_DrawLevelField(x, y);
6563   }
6564
6565   if (impact && CAN_EXPLODE_IMPACT(element))
6566   {
6567     Bang(x, y);
6568     return;
6569   }
6570   else if (impact && element == EL_PEARL &&
6571            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6572   {
6573     ResetGfxAnimation(x, y);
6574
6575     Tile[x][y] = EL_PEARL_BREAKING;
6576     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6577     return;
6578   }
6579   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6580   {
6581     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6582
6583     return;
6584   }
6585
6586   if (impact && element == EL_AMOEBA_DROP)
6587   {
6588     if (object_hit && IS_PLAYER(x, y + 1))
6589       KillPlayerUnlessEnemyProtected(x, y + 1);
6590     else if (object_hit && smashed == EL_PENGUIN)
6591       Bang(x, y + 1);
6592     else
6593     {
6594       Tile[x][y] = EL_AMOEBA_GROWING;
6595       Store[x][y] = EL_AMOEBA_WET;
6596
6597       ResetRandomAnimationValue(x, y);
6598     }
6599     return;
6600   }
6601
6602   if (object_hit)               // check which object was hit
6603   {
6604     if ((CAN_PASS_MAGIC_WALL(element) && 
6605          (smashed == EL_MAGIC_WALL ||
6606           smashed == EL_BD_MAGIC_WALL)) ||
6607         (CAN_PASS_DC_MAGIC_WALL(element) &&
6608          smashed == EL_DC_MAGIC_WALL))
6609     {
6610       int xx, yy;
6611       int activated_magic_wall =
6612         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6613          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6614          EL_DC_MAGIC_WALL_ACTIVE);
6615
6616       // activate magic wall / mill
6617       SCAN_PLAYFIELD(xx, yy)
6618       {
6619         if (Tile[xx][yy] == smashed)
6620           Tile[xx][yy] = activated_magic_wall;
6621       }
6622
6623       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6624       game.magic_wall_active = TRUE;
6625
6626       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6627                             SND_MAGIC_WALL_ACTIVATING :
6628                             smashed == EL_BD_MAGIC_WALL ?
6629                             SND_BD_MAGIC_WALL_ACTIVATING :
6630                             SND_DC_MAGIC_WALL_ACTIVATING));
6631     }
6632
6633     if (IS_PLAYER(x, y + 1))
6634     {
6635       if (CAN_SMASH_PLAYER(element))
6636       {
6637         KillPlayerUnlessEnemyProtected(x, y + 1);
6638         return;
6639       }
6640     }
6641     else if (smashed == EL_PENGUIN)
6642     {
6643       if (CAN_SMASH_PLAYER(element))
6644       {
6645         Bang(x, y + 1);
6646         return;
6647       }
6648     }
6649     else if (element == EL_BD_DIAMOND)
6650     {
6651       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6652       {
6653         Bang(x, y + 1);
6654         return;
6655       }
6656     }
6657     else if (((element == EL_SP_INFOTRON ||
6658                element == EL_SP_ZONK) &&
6659               (smashed == EL_SP_SNIKSNAK ||
6660                smashed == EL_SP_ELECTRON ||
6661                smashed == EL_SP_DISK_ORANGE)) ||
6662              (element == EL_SP_INFOTRON &&
6663               smashed == EL_SP_DISK_YELLOW))
6664     {
6665       Bang(x, y + 1);
6666       return;
6667     }
6668     else if (CAN_SMASH_EVERYTHING(element))
6669     {
6670       if (IS_CLASSIC_ENEMY(smashed) ||
6671           CAN_EXPLODE_SMASHED(smashed))
6672       {
6673         Bang(x, y + 1);
6674         return;
6675       }
6676       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6677       {
6678         if (smashed == EL_LAMP ||
6679             smashed == EL_LAMP_ACTIVE)
6680         {
6681           Bang(x, y + 1);
6682           return;
6683         }
6684         else if (smashed == EL_NUT)
6685         {
6686           Tile[x][y + 1] = EL_NUT_BREAKING;
6687           PlayLevelSound(x, y, SND_NUT_BREAKING);
6688           RaiseScoreElement(EL_NUT);
6689           return;
6690         }
6691         else if (smashed == EL_PEARL)
6692         {
6693           ResetGfxAnimation(x, y);
6694
6695           Tile[x][y + 1] = EL_PEARL_BREAKING;
6696           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6697           return;
6698         }
6699         else if (smashed == EL_DIAMOND)
6700         {
6701           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6702           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6703           return;
6704         }
6705         else if (IS_BELT_SWITCH(smashed))
6706         {
6707           ToggleBeltSwitch(x, y + 1);
6708         }
6709         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6710                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6711                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6712                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6713         {
6714           ToggleSwitchgateSwitch(x, y + 1);
6715         }
6716         else if (smashed == EL_LIGHT_SWITCH ||
6717                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6718         {
6719           ToggleLightSwitch(x, y + 1);
6720         }
6721         else
6722         {
6723           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6724
6725           CheckElementChangeBySide(x, y + 1, smashed, element,
6726                                    CE_SWITCHED, CH_SIDE_TOP);
6727           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6728                                             CH_SIDE_TOP);
6729         }
6730       }
6731       else
6732       {
6733         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6734       }
6735     }
6736   }
6737
6738   // play sound of magic wall / mill
6739   if (!last_line &&
6740       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6741        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6742        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6743   {
6744     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6745       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6746     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6747       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6748     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6749       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6750
6751     return;
6752   }
6753
6754   // play sound of object that hits the ground
6755   if (last_line || object_hit)
6756     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6757 }
6758
6759 static void TurnRoundExt(int x, int y)
6760 {
6761   static struct
6762   {
6763     int dx, dy;
6764   } move_xy[] =
6765   {
6766     {  0,  0 },
6767     { -1,  0 },
6768     { +1,  0 },
6769     {  0,  0 },
6770     {  0, -1 },
6771     {  0,  0 }, { 0, 0 }, { 0, 0 },
6772     {  0, +1 }
6773   };
6774   static struct
6775   {
6776     int left, right, back;
6777   } turn[] =
6778   {
6779     { 0,        0,              0        },
6780     { MV_DOWN,  MV_UP,          MV_RIGHT },
6781     { MV_UP,    MV_DOWN,        MV_LEFT  },
6782     { 0,        0,              0        },
6783     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6784     { 0,        0,              0        },
6785     { 0,        0,              0        },
6786     { 0,        0,              0        },
6787     { MV_RIGHT, MV_LEFT,        MV_UP    }
6788   };
6789
6790   int element = Tile[x][y];
6791   int move_pattern = element_info[element].move_pattern;
6792
6793   int old_move_dir = MovDir[x][y];
6794   int left_dir  = turn[old_move_dir].left;
6795   int right_dir = turn[old_move_dir].right;
6796   int back_dir  = turn[old_move_dir].back;
6797
6798   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6799   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6800   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6801   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6802
6803   int left_x  = x + left_dx,  left_y  = y + left_dy;
6804   int right_x = x + right_dx, right_y = y + right_dy;
6805   int move_x  = x + move_dx,  move_y  = y + move_dy;
6806
6807   int xx, yy;
6808
6809   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6810   {
6811     TestIfBadThingTouchesOtherBadThing(x, y);
6812
6813     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6814       MovDir[x][y] = right_dir;
6815     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6816       MovDir[x][y] = left_dir;
6817
6818     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6819       MovDelay[x][y] = 9;
6820     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6821       MovDelay[x][y] = 1;
6822   }
6823   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6824   {
6825     TestIfBadThingTouchesOtherBadThing(x, y);
6826
6827     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6828       MovDir[x][y] = left_dir;
6829     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6830       MovDir[x][y] = right_dir;
6831
6832     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6833       MovDelay[x][y] = 9;
6834     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6835       MovDelay[x][y] = 1;
6836   }
6837   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6838   {
6839     TestIfBadThingTouchesOtherBadThing(x, y);
6840
6841     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6842       MovDir[x][y] = left_dir;
6843     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6844       MovDir[x][y] = right_dir;
6845
6846     if (MovDir[x][y] != old_move_dir)
6847       MovDelay[x][y] = 9;
6848   }
6849   else if (element == EL_YAMYAM)
6850   {
6851     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6852     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6853
6854     if (can_turn_left && can_turn_right)
6855       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6856     else if (can_turn_left)
6857       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6858     else if (can_turn_right)
6859       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6860     else
6861       MovDir[x][y] = back_dir;
6862
6863     MovDelay[x][y] = 16 + 16 * RND(3);
6864   }
6865   else if (element == EL_DARK_YAMYAM)
6866   {
6867     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6868                                                          left_x, left_y);
6869     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6870                                                          right_x, right_y);
6871
6872     if (can_turn_left && can_turn_right)
6873       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6874     else if (can_turn_left)
6875       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6876     else if (can_turn_right)
6877       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6878     else
6879       MovDir[x][y] = back_dir;
6880
6881     MovDelay[x][y] = 16 + 16 * RND(3);
6882   }
6883   else if (element == EL_PACMAN)
6884   {
6885     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6886     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6887
6888     if (can_turn_left && can_turn_right)
6889       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6890     else if (can_turn_left)
6891       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6892     else if (can_turn_right)
6893       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6894     else
6895       MovDir[x][y] = back_dir;
6896
6897     MovDelay[x][y] = 6 + RND(40);
6898   }
6899   else if (element == EL_PIG)
6900   {
6901     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6902     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6903     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6904     boolean should_turn_left, should_turn_right, should_move_on;
6905     int rnd_value = 24;
6906     int rnd = RND(rnd_value);
6907
6908     should_turn_left = (can_turn_left &&
6909                         (!can_move_on ||
6910                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6911                                                    y + back_dy + left_dy)));
6912     should_turn_right = (can_turn_right &&
6913                          (!can_move_on ||
6914                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6915                                                     y + back_dy + right_dy)));
6916     should_move_on = (can_move_on &&
6917                       (!can_turn_left ||
6918                        !can_turn_right ||
6919                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6920                                                  y + move_dy + left_dy) ||
6921                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6922                                                  y + move_dy + right_dy)));
6923
6924     if (should_turn_left || should_turn_right || should_move_on)
6925     {
6926       if (should_turn_left && should_turn_right && should_move_on)
6927         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6928                         rnd < 2 * rnd_value / 3 ? right_dir :
6929                         old_move_dir);
6930       else if (should_turn_left && should_turn_right)
6931         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6932       else if (should_turn_left && should_move_on)
6933         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6934       else if (should_turn_right && should_move_on)
6935         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6936       else if (should_turn_left)
6937         MovDir[x][y] = left_dir;
6938       else if (should_turn_right)
6939         MovDir[x][y] = right_dir;
6940       else if (should_move_on)
6941         MovDir[x][y] = old_move_dir;
6942     }
6943     else if (can_move_on && rnd > rnd_value / 8)
6944       MovDir[x][y] = old_move_dir;
6945     else if (can_turn_left && can_turn_right)
6946       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6947     else if (can_turn_left && rnd > rnd_value / 8)
6948       MovDir[x][y] = left_dir;
6949     else if (can_turn_right && rnd > rnd_value/8)
6950       MovDir[x][y] = right_dir;
6951     else
6952       MovDir[x][y] = back_dir;
6953
6954     xx = x + move_xy[MovDir[x][y]].dx;
6955     yy = y + move_xy[MovDir[x][y]].dy;
6956
6957     if (!IN_LEV_FIELD(xx, yy) ||
6958         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
6959       MovDir[x][y] = old_move_dir;
6960
6961     MovDelay[x][y] = 0;
6962   }
6963   else if (element == EL_DRAGON)
6964   {
6965     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6966     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6967     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6968     int rnd_value = 24;
6969     int rnd = RND(rnd_value);
6970
6971     if (can_move_on && rnd > rnd_value / 8)
6972       MovDir[x][y] = old_move_dir;
6973     else if (can_turn_left && can_turn_right)
6974       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6975     else if (can_turn_left && rnd > rnd_value / 8)
6976       MovDir[x][y] = left_dir;
6977     else if (can_turn_right && rnd > rnd_value / 8)
6978       MovDir[x][y] = right_dir;
6979     else
6980       MovDir[x][y] = back_dir;
6981
6982     xx = x + move_xy[MovDir[x][y]].dx;
6983     yy = y + move_xy[MovDir[x][y]].dy;
6984
6985     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6986       MovDir[x][y] = old_move_dir;
6987
6988     MovDelay[x][y] = 0;
6989   }
6990   else if (element == EL_MOLE)
6991   {
6992     boolean can_move_on =
6993       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6994                             IS_AMOEBOID(Tile[move_x][move_y]) ||
6995                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
6996     if (!can_move_on)
6997     {
6998       boolean can_turn_left =
6999         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7000                               IS_AMOEBOID(Tile[left_x][left_y])));
7001
7002       boolean can_turn_right =
7003         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7004                               IS_AMOEBOID(Tile[right_x][right_y])));
7005
7006       if (can_turn_left && can_turn_right)
7007         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7008       else if (can_turn_left)
7009         MovDir[x][y] = left_dir;
7010       else
7011         MovDir[x][y] = right_dir;
7012     }
7013
7014     if (MovDir[x][y] != old_move_dir)
7015       MovDelay[x][y] = 9;
7016   }
7017   else if (element == EL_BALLOON)
7018   {
7019     MovDir[x][y] = game.wind_direction;
7020     MovDelay[x][y] = 0;
7021   }
7022   else if (element == EL_SPRING)
7023   {
7024     if (MovDir[x][y] & MV_HORIZONTAL)
7025     {
7026       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7027           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7028       {
7029         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7030         ResetGfxAnimation(move_x, move_y);
7031         TEST_DrawLevelField(move_x, move_y);
7032
7033         MovDir[x][y] = back_dir;
7034       }
7035       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7036                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7037         MovDir[x][y] = MV_NONE;
7038     }
7039
7040     MovDelay[x][y] = 0;
7041   }
7042   else if (element == EL_ROBOT ||
7043            element == EL_SATELLITE ||
7044            element == EL_PENGUIN ||
7045            element == EL_EMC_ANDROID)
7046   {
7047     int attr_x = -1, attr_y = -1;
7048
7049     if (game.all_players_gone)
7050     {
7051       attr_x = game.exit_x;
7052       attr_y = game.exit_y;
7053     }
7054     else
7055     {
7056       int i;
7057
7058       for (i = 0; i < MAX_PLAYERS; i++)
7059       {
7060         struct PlayerInfo *player = &stored_player[i];
7061         int jx = player->jx, jy = player->jy;
7062
7063         if (!player->active)
7064           continue;
7065
7066         if (attr_x == -1 ||
7067             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7068         {
7069           attr_x = jx;
7070           attr_y = jy;
7071         }
7072       }
7073     }
7074
7075     if (element == EL_ROBOT &&
7076         game.robot_wheel_x >= 0 &&
7077         game.robot_wheel_y >= 0 &&
7078         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7079          game.engine_version < VERSION_IDENT(3,1,0,0)))
7080     {
7081       attr_x = game.robot_wheel_x;
7082       attr_y = game.robot_wheel_y;
7083     }
7084
7085     if (element == EL_PENGUIN)
7086     {
7087       int i;
7088       static int xy[4][2] =
7089       {
7090         { 0, -1 },
7091         { -1, 0 },
7092         { +1, 0 },
7093         { 0, +1 }
7094       };
7095
7096       for (i = 0; i < NUM_DIRECTIONS; i++)
7097       {
7098         int ex = x + xy[i][0];
7099         int ey = y + xy[i][1];
7100
7101         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7102                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7103                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7104                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7105         {
7106           attr_x = ex;
7107           attr_y = ey;
7108           break;
7109         }
7110       }
7111     }
7112
7113     MovDir[x][y] = MV_NONE;
7114     if (attr_x < x)
7115       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7116     else if (attr_x > x)
7117       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7118     if (attr_y < y)
7119       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7120     else if (attr_y > y)
7121       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7122
7123     if (element == EL_ROBOT)
7124     {
7125       int newx, newy;
7126
7127       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7128         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7129       Moving2Blocked(x, y, &newx, &newy);
7130
7131       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7132         MovDelay[x][y] = 8 + 8 * !RND(3);
7133       else
7134         MovDelay[x][y] = 16;
7135     }
7136     else if (element == EL_PENGUIN)
7137     {
7138       int newx, newy;
7139
7140       MovDelay[x][y] = 1;
7141
7142       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7143       {
7144         boolean first_horiz = RND(2);
7145         int new_move_dir = MovDir[x][y];
7146
7147         MovDir[x][y] =
7148           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7149         Moving2Blocked(x, y, &newx, &newy);
7150
7151         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7152           return;
7153
7154         MovDir[x][y] =
7155           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7156         Moving2Blocked(x, y, &newx, &newy);
7157
7158         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7159           return;
7160
7161         MovDir[x][y] = old_move_dir;
7162         return;
7163       }
7164     }
7165     else if (element == EL_SATELLITE)
7166     {
7167       int newx, newy;
7168
7169       MovDelay[x][y] = 1;
7170
7171       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7172       {
7173         boolean first_horiz = RND(2);
7174         int new_move_dir = MovDir[x][y];
7175
7176         MovDir[x][y] =
7177           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7178         Moving2Blocked(x, y, &newx, &newy);
7179
7180         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7181           return;
7182
7183         MovDir[x][y] =
7184           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7185         Moving2Blocked(x, y, &newx, &newy);
7186
7187         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7188           return;
7189
7190         MovDir[x][y] = old_move_dir;
7191         return;
7192       }
7193     }
7194     else if (element == EL_EMC_ANDROID)
7195     {
7196       static int check_pos[16] =
7197       {
7198         -1,             //  0 => (invalid)
7199         7,              //  1 => MV_LEFT
7200         3,              //  2 => MV_RIGHT
7201         -1,             //  3 => (invalid)
7202         1,              //  4 =>            MV_UP
7203         0,              //  5 => MV_LEFT  | MV_UP
7204         2,              //  6 => MV_RIGHT | MV_UP
7205         -1,             //  7 => (invalid)
7206         5,              //  8 =>            MV_DOWN
7207         6,              //  9 => MV_LEFT  | MV_DOWN
7208         4,              // 10 => MV_RIGHT | MV_DOWN
7209         -1,             // 11 => (invalid)
7210         -1,             // 12 => (invalid)
7211         -1,             // 13 => (invalid)
7212         -1,             // 14 => (invalid)
7213         -1,             // 15 => (invalid)
7214       };
7215       static struct
7216       {
7217         int dx, dy;
7218         int dir;
7219       } check_xy[8] =
7220       {
7221         { -1, -1,       MV_LEFT  | MV_UP   },
7222         {  0, -1,                  MV_UP   },
7223         { +1, -1,       MV_RIGHT | MV_UP   },
7224         { +1,  0,       MV_RIGHT           },
7225         { +1, +1,       MV_RIGHT | MV_DOWN },
7226         {  0, +1,                  MV_DOWN },
7227         { -1, +1,       MV_LEFT  | MV_DOWN },
7228         { -1,  0,       MV_LEFT            },
7229       };
7230       int start_pos, check_order;
7231       boolean can_clone = FALSE;
7232       int i;
7233
7234       // check if there is any free field around current position
7235       for (i = 0; i < 8; i++)
7236       {
7237         int newx = x + check_xy[i].dx;
7238         int newy = y + check_xy[i].dy;
7239
7240         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7241         {
7242           can_clone = TRUE;
7243
7244           break;
7245         }
7246       }
7247
7248       if (can_clone)            // randomly find an element to clone
7249       {
7250         can_clone = FALSE;
7251
7252         start_pos = check_pos[RND(8)];
7253         check_order = (RND(2) ? -1 : +1);
7254
7255         for (i = 0; i < 8; i++)
7256         {
7257           int pos_raw = start_pos + i * check_order;
7258           int pos = (pos_raw + 8) % 8;
7259           int newx = x + check_xy[pos].dx;
7260           int newy = y + check_xy[pos].dy;
7261
7262           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7263           {
7264             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7265             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7266
7267             Store[x][y] = Tile[newx][newy];
7268
7269             can_clone = TRUE;
7270
7271             break;
7272           }
7273         }
7274       }
7275
7276       if (can_clone)            // randomly find a direction to move
7277       {
7278         can_clone = FALSE;
7279
7280         start_pos = check_pos[RND(8)];
7281         check_order = (RND(2) ? -1 : +1);
7282
7283         for (i = 0; i < 8; i++)
7284         {
7285           int pos_raw = start_pos + i * check_order;
7286           int pos = (pos_raw + 8) % 8;
7287           int newx = x + check_xy[pos].dx;
7288           int newy = y + check_xy[pos].dy;
7289           int new_move_dir = check_xy[pos].dir;
7290
7291           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7292           {
7293             MovDir[x][y] = new_move_dir;
7294             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7295
7296             can_clone = TRUE;
7297
7298             break;
7299           }
7300         }
7301       }
7302
7303       if (can_clone)            // cloning and moving successful
7304         return;
7305
7306       // cannot clone -- try to move towards player
7307
7308       start_pos = check_pos[MovDir[x][y] & 0x0f];
7309       check_order = (RND(2) ? -1 : +1);
7310
7311       for (i = 0; i < 3; i++)
7312       {
7313         // first check start_pos, then previous/next or (next/previous) pos
7314         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7315         int pos = (pos_raw + 8) % 8;
7316         int newx = x + check_xy[pos].dx;
7317         int newy = y + check_xy[pos].dy;
7318         int new_move_dir = check_xy[pos].dir;
7319
7320         if (IS_PLAYER(newx, newy))
7321           break;
7322
7323         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7324         {
7325           MovDir[x][y] = new_move_dir;
7326           MovDelay[x][y] = level.android_move_time * 8 + 1;
7327
7328           break;
7329         }
7330       }
7331     }
7332   }
7333   else if (move_pattern == MV_TURNING_LEFT ||
7334            move_pattern == MV_TURNING_RIGHT ||
7335            move_pattern == MV_TURNING_LEFT_RIGHT ||
7336            move_pattern == MV_TURNING_RIGHT_LEFT ||
7337            move_pattern == MV_TURNING_RANDOM ||
7338            move_pattern == MV_ALL_DIRECTIONS)
7339   {
7340     boolean can_turn_left =
7341       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7342     boolean can_turn_right =
7343       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7344
7345     if (element_info[element].move_stepsize == 0)       // "not moving"
7346       return;
7347
7348     if (move_pattern == MV_TURNING_LEFT)
7349       MovDir[x][y] = left_dir;
7350     else if (move_pattern == MV_TURNING_RIGHT)
7351       MovDir[x][y] = right_dir;
7352     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7353       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7354     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7355       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7356     else if (move_pattern == MV_TURNING_RANDOM)
7357       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7358                       can_turn_right && !can_turn_left ? right_dir :
7359                       RND(2) ? left_dir : right_dir);
7360     else if (can_turn_left && can_turn_right)
7361       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7362     else if (can_turn_left)
7363       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7364     else if (can_turn_right)
7365       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7366     else
7367       MovDir[x][y] = back_dir;
7368
7369     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7370   }
7371   else if (move_pattern == MV_HORIZONTAL ||
7372            move_pattern == MV_VERTICAL)
7373   {
7374     if (move_pattern & old_move_dir)
7375       MovDir[x][y] = back_dir;
7376     else if (move_pattern == MV_HORIZONTAL)
7377       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7378     else if (move_pattern == MV_VERTICAL)
7379       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7380
7381     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7382   }
7383   else if (move_pattern & MV_ANY_DIRECTION)
7384   {
7385     MovDir[x][y] = move_pattern;
7386     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7387   }
7388   else if (move_pattern & MV_WIND_DIRECTION)
7389   {
7390     MovDir[x][y] = game.wind_direction;
7391     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7392   }
7393   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7394   {
7395     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7396       MovDir[x][y] = left_dir;
7397     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7398       MovDir[x][y] = right_dir;
7399
7400     if (MovDir[x][y] != old_move_dir)
7401       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7402   }
7403   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7404   {
7405     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7406       MovDir[x][y] = right_dir;
7407     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7408       MovDir[x][y] = left_dir;
7409
7410     if (MovDir[x][y] != old_move_dir)
7411       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7412   }
7413   else if (move_pattern == MV_TOWARDS_PLAYER ||
7414            move_pattern == MV_AWAY_FROM_PLAYER)
7415   {
7416     int attr_x = -1, attr_y = -1;
7417     int newx, newy;
7418     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7419
7420     if (game.all_players_gone)
7421     {
7422       attr_x = game.exit_x;
7423       attr_y = game.exit_y;
7424     }
7425     else
7426     {
7427       int i;
7428
7429       for (i = 0; i < MAX_PLAYERS; i++)
7430       {
7431         struct PlayerInfo *player = &stored_player[i];
7432         int jx = player->jx, jy = player->jy;
7433
7434         if (!player->active)
7435           continue;
7436
7437         if (attr_x == -1 ||
7438             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7439         {
7440           attr_x = jx;
7441           attr_y = jy;
7442         }
7443       }
7444     }
7445
7446     MovDir[x][y] = MV_NONE;
7447     if (attr_x < x)
7448       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7449     else if (attr_x > x)
7450       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7451     if (attr_y < y)
7452       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7453     else if (attr_y > y)
7454       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7455
7456     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7457
7458     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7459     {
7460       boolean first_horiz = RND(2);
7461       int new_move_dir = MovDir[x][y];
7462
7463       if (element_info[element].move_stepsize == 0)     // "not moving"
7464       {
7465         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7466         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7467
7468         return;
7469       }
7470
7471       MovDir[x][y] =
7472         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7473       Moving2Blocked(x, y, &newx, &newy);
7474
7475       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7476         return;
7477
7478       MovDir[x][y] =
7479         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7480       Moving2Blocked(x, y, &newx, &newy);
7481
7482       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7483         return;
7484
7485       MovDir[x][y] = old_move_dir;
7486     }
7487   }
7488   else if (move_pattern == MV_WHEN_PUSHED ||
7489            move_pattern == MV_WHEN_DROPPED)
7490   {
7491     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7492       MovDir[x][y] = MV_NONE;
7493
7494     MovDelay[x][y] = 0;
7495   }
7496   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7497   {
7498     static int test_xy[7][2] =
7499     {
7500       { 0, -1 },
7501       { -1, 0 },
7502       { +1, 0 },
7503       { 0, +1 },
7504       { 0, -1 },
7505       { -1, 0 },
7506       { +1, 0 },
7507     };
7508     static int test_dir[7] =
7509     {
7510       MV_UP,
7511       MV_LEFT,
7512       MV_RIGHT,
7513       MV_DOWN,
7514       MV_UP,
7515       MV_LEFT,
7516       MV_RIGHT,
7517     };
7518     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7519     int move_preference = -1000000;     // start with very low preference
7520     int new_move_dir = MV_NONE;
7521     int start_test = RND(4);
7522     int i;
7523
7524     for (i = 0; i < NUM_DIRECTIONS; i++)
7525     {
7526       int move_dir = test_dir[start_test + i];
7527       int move_dir_preference;
7528
7529       xx = x + test_xy[start_test + i][0];
7530       yy = y + test_xy[start_test + i][1];
7531
7532       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7533           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7534       {
7535         new_move_dir = move_dir;
7536
7537         break;
7538       }
7539
7540       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7541         continue;
7542
7543       move_dir_preference = -1 * RunnerVisit[xx][yy];
7544       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7545         move_dir_preference = PlayerVisit[xx][yy];
7546
7547       if (move_dir_preference > move_preference)
7548       {
7549         // prefer field that has not been visited for the longest time
7550         move_preference = move_dir_preference;
7551         new_move_dir = move_dir;
7552       }
7553       else if (move_dir_preference == move_preference &&
7554                move_dir == old_move_dir)
7555       {
7556         // prefer last direction when all directions are preferred equally
7557         move_preference = move_dir_preference;
7558         new_move_dir = move_dir;
7559       }
7560     }
7561
7562     MovDir[x][y] = new_move_dir;
7563     if (old_move_dir != new_move_dir)
7564       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7565   }
7566 }
7567
7568 static void TurnRound(int x, int y)
7569 {
7570   int direction = MovDir[x][y];
7571
7572   TurnRoundExt(x, y);
7573
7574   GfxDir[x][y] = MovDir[x][y];
7575
7576   if (direction != MovDir[x][y])
7577     GfxFrame[x][y] = 0;
7578
7579   if (MovDelay[x][y])
7580     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7581
7582   ResetGfxFrame(x, y);
7583 }
7584
7585 static boolean JustBeingPushed(int x, int y)
7586 {
7587   int i;
7588
7589   for (i = 0; i < MAX_PLAYERS; i++)
7590   {
7591     struct PlayerInfo *player = &stored_player[i];
7592
7593     if (player->active && player->is_pushing && player->MovPos)
7594     {
7595       int next_jx = player->jx + (player->jx - player->last_jx);
7596       int next_jy = player->jy + (player->jy - player->last_jy);
7597
7598       if (x == next_jx && y == next_jy)
7599         return TRUE;
7600     }
7601   }
7602
7603   return FALSE;
7604 }
7605
7606 static void StartMoving(int x, int y)
7607 {
7608   boolean started_moving = FALSE;       // some elements can fall _and_ move
7609   int element = Tile[x][y];
7610
7611   if (Stop[x][y])
7612     return;
7613
7614   if (MovDelay[x][y] == 0)
7615     GfxAction[x][y] = ACTION_DEFAULT;
7616
7617   if (CAN_FALL(element) && y < lev_fieldy - 1)
7618   {
7619     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7620         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7621       if (JustBeingPushed(x, y))
7622         return;
7623
7624     if (element == EL_QUICKSAND_FULL)
7625     {
7626       if (IS_FREE(x, y + 1))
7627       {
7628         InitMovingField(x, y, MV_DOWN);
7629         started_moving = TRUE;
7630
7631         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7632 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7633         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7634           Store[x][y] = EL_ROCK;
7635 #else
7636         Store[x][y] = EL_ROCK;
7637 #endif
7638
7639         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7640       }
7641       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7642       {
7643         if (!MovDelay[x][y])
7644         {
7645           MovDelay[x][y] = TILEY + 1;
7646
7647           ResetGfxAnimation(x, y);
7648           ResetGfxAnimation(x, y + 1);
7649         }
7650
7651         if (MovDelay[x][y])
7652         {
7653           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7654           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7655
7656           MovDelay[x][y]--;
7657           if (MovDelay[x][y])
7658             return;
7659         }
7660
7661         Tile[x][y] = EL_QUICKSAND_EMPTY;
7662         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7663         Store[x][y + 1] = Store[x][y];
7664         Store[x][y] = 0;
7665
7666         PlayLevelSoundAction(x, y, ACTION_FILLING);
7667       }
7668       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7669       {
7670         if (!MovDelay[x][y])
7671         {
7672           MovDelay[x][y] = TILEY + 1;
7673
7674           ResetGfxAnimation(x, y);
7675           ResetGfxAnimation(x, y + 1);
7676         }
7677
7678         if (MovDelay[x][y])
7679         {
7680           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7681           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7682
7683           MovDelay[x][y]--;
7684           if (MovDelay[x][y])
7685             return;
7686         }
7687
7688         Tile[x][y] = EL_QUICKSAND_EMPTY;
7689         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7690         Store[x][y + 1] = Store[x][y];
7691         Store[x][y] = 0;
7692
7693         PlayLevelSoundAction(x, y, ACTION_FILLING);
7694       }
7695     }
7696     else if (element == EL_QUICKSAND_FAST_FULL)
7697     {
7698       if (IS_FREE(x, y + 1))
7699       {
7700         InitMovingField(x, y, MV_DOWN);
7701         started_moving = TRUE;
7702
7703         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7704 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7705         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7706           Store[x][y] = EL_ROCK;
7707 #else
7708         Store[x][y] = EL_ROCK;
7709 #endif
7710
7711         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7712       }
7713       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7714       {
7715         if (!MovDelay[x][y])
7716         {
7717           MovDelay[x][y] = TILEY + 1;
7718
7719           ResetGfxAnimation(x, y);
7720           ResetGfxAnimation(x, y + 1);
7721         }
7722
7723         if (MovDelay[x][y])
7724         {
7725           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7726           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7727
7728           MovDelay[x][y]--;
7729           if (MovDelay[x][y])
7730             return;
7731         }
7732
7733         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7734         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7735         Store[x][y + 1] = Store[x][y];
7736         Store[x][y] = 0;
7737
7738         PlayLevelSoundAction(x, y, ACTION_FILLING);
7739       }
7740       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7741       {
7742         if (!MovDelay[x][y])
7743         {
7744           MovDelay[x][y] = TILEY + 1;
7745
7746           ResetGfxAnimation(x, y);
7747           ResetGfxAnimation(x, y + 1);
7748         }
7749
7750         if (MovDelay[x][y])
7751         {
7752           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7753           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7754
7755           MovDelay[x][y]--;
7756           if (MovDelay[x][y])
7757             return;
7758         }
7759
7760         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7761         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7762         Store[x][y + 1] = Store[x][y];
7763         Store[x][y] = 0;
7764
7765         PlayLevelSoundAction(x, y, ACTION_FILLING);
7766       }
7767     }
7768     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7769              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7770     {
7771       InitMovingField(x, y, MV_DOWN);
7772       started_moving = TRUE;
7773
7774       Tile[x][y] = EL_QUICKSAND_FILLING;
7775       Store[x][y] = element;
7776
7777       PlayLevelSoundAction(x, y, ACTION_FILLING);
7778     }
7779     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7780              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7781     {
7782       InitMovingField(x, y, MV_DOWN);
7783       started_moving = TRUE;
7784
7785       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7786       Store[x][y] = element;
7787
7788       PlayLevelSoundAction(x, y, ACTION_FILLING);
7789     }
7790     else if (element == EL_MAGIC_WALL_FULL)
7791     {
7792       if (IS_FREE(x, y + 1))
7793       {
7794         InitMovingField(x, y, MV_DOWN);
7795         started_moving = TRUE;
7796
7797         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7798         Store[x][y] = EL_CHANGED(Store[x][y]);
7799       }
7800       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7801       {
7802         if (!MovDelay[x][y])
7803           MovDelay[x][y] = TILEY / 4 + 1;
7804
7805         if (MovDelay[x][y])
7806         {
7807           MovDelay[x][y]--;
7808           if (MovDelay[x][y])
7809             return;
7810         }
7811
7812         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7813         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7814         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7815         Store[x][y] = 0;
7816       }
7817     }
7818     else if (element == EL_BD_MAGIC_WALL_FULL)
7819     {
7820       if (IS_FREE(x, y + 1))
7821       {
7822         InitMovingField(x, y, MV_DOWN);
7823         started_moving = TRUE;
7824
7825         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7826         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7827       }
7828       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7829       {
7830         if (!MovDelay[x][y])
7831           MovDelay[x][y] = TILEY / 4 + 1;
7832
7833         if (MovDelay[x][y])
7834         {
7835           MovDelay[x][y]--;
7836           if (MovDelay[x][y])
7837             return;
7838         }
7839
7840         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7841         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7842         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7843         Store[x][y] = 0;
7844       }
7845     }
7846     else if (element == EL_DC_MAGIC_WALL_FULL)
7847     {
7848       if (IS_FREE(x, y + 1))
7849       {
7850         InitMovingField(x, y, MV_DOWN);
7851         started_moving = TRUE;
7852
7853         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7854         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7855       }
7856       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7857       {
7858         if (!MovDelay[x][y])
7859           MovDelay[x][y] = TILEY / 4 + 1;
7860
7861         if (MovDelay[x][y])
7862         {
7863           MovDelay[x][y]--;
7864           if (MovDelay[x][y])
7865             return;
7866         }
7867
7868         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7869         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7870         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7871         Store[x][y] = 0;
7872       }
7873     }
7874     else if ((CAN_PASS_MAGIC_WALL(element) &&
7875               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7876                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7877              (CAN_PASS_DC_MAGIC_WALL(element) &&
7878               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7879
7880     {
7881       InitMovingField(x, y, MV_DOWN);
7882       started_moving = TRUE;
7883
7884       Tile[x][y] =
7885         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7886          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7887          EL_DC_MAGIC_WALL_FILLING);
7888       Store[x][y] = element;
7889     }
7890     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
7891     {
7892       SplashAcid(x, y + 1);
7893
7894       InitMovingField(x, y, MV_DOWN);
7895       started_moving = TRUE;
7896
7897       Store[x][y] = EL_ACID;
7898     }
7899     else if (
7900              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7901               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7902              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7903               CAN_FALL(element) && WasJustFalling[x][y] &&
7904               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7905
7906              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7907               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7908               (Tile[x][y + 1] == EL_BLOCKED)))
7909     {
7910       /* this is needed for a special case not covered by calling "Impact()"
7911          from "ContinueMoving()": if an element moves to a tile directly below
7912          another element which was just falling on that tile (which was empty
7913          in the previous frame), the falling element above would just stop
7914          instead of smashing the element below (in previous version, the above
7915          element was just checked for "moving" instead of "falling", resulting
7916          in incorrect smashes caused by horizontal movement of the above
7917          element; also, the case of the player being the element to smash was
7918          simply not covered here... :-/ ) */
7919
7920       CheckCollision[x][y] = 0;
7921       CheckImpact[x][y] = 0;
7922
7923       Impact(x, y);
7924     }
7925     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7926     {
7927       if (MovDir[x][y] == MV_NONE)
7928       {
7929         InitMovingField(x, y, MV_DOWN);
7930         started_moving = TRUE;
7931       }
7932     }
7933     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
7934     {
7935       if (WasJustFalling[x][y]) // prevent animation from being restarted
7936         MovDir[x][y] = MV_DOWN;
7937
7938       InitMovingField(x, y, MV_DOWN);
7939       started_moving = TRUE;
7940     }
7941     else if (element == EL_AMOEBA_DROP)
7942     {
7943       Tile[x][y] = EL_AMOEBA_GROWING;
7944       Store[x][y] = EL_AMOEBA_WET;
7945     }
7946     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7947               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
7948              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7949              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7950     {
7951       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7952                                 (IS_FREE(x - 1, y + 1) ||
7953                                  Tile[x - 1][y + 1] == EL_ACID));
7954       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7955                                 (IS_FREE(x + 1, y + 1) ||
7956                                  Tile[x + 1][y + 1] == EL_ACID));
7957       boolean can_fall_any  = (can_fall_left || can_fall_right);
7958       boolean can_fall_both = (can_fall_left && can_fall_right);
7959       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
7960
7961       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7962       {
7963         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7964           can_fall_right = FALSE;
7965         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7966           can_fall_left = FALSE;
7967         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7968           can_fall_right = FALSE;
7969         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7970           can_fall_left = FALSE;
7971
7972         can_fall_any  = (can_fall_left || can_fall_right);
7973         can_fall_both = FALSE;
7974       }
7975
7976       if (can_fall_both)
7977       {
7978         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7979           can_fall_right = FALSE;       // slip down on left side
7980         else
7981           can_fall_left = !(can_fall_right = RND(2));
7982
7983         can_fall_both = FALSE;
7984       }
7985
7986       if (can_fall_any)
7987       {
7988         // if not determined otherwise, prefer left side for slipping down
7989         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7990         started_moving = TRUE;
7991       }
7992     }
7993     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
7994     {
7995       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7996       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7997       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
7998       int belt_dir = game.belt_dir[belt_nr];
7999
8000       if ((belt_dir == MV_LEFT  && left_is_free) ||
8001           (belt_dir == MV_RIGHT && right_is_free))
8002       {
8003         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8004
8005         InitMovingField(x, y, belt_dir);
8006         started_moving = TRUE;
8007
8008         Pushed[x][y] = TRUE;
8009         Pushed[nextx][y] = TRUE;
8010
8011         GfxAction[x][y] = ACTION_DEFAULT;
8012       }
8013       else
8014       {
8015         MovDir[x][y] = 0;       // if element was moving, stop it
8016       }
8017     }
8018   }
8019
8020   // not "else if" because of elements that can fall and move (EL_SPRING)
8021   if (CAN_MOVE(element) && !started_moving)
8022   {
8023     int move_pattern = element_info[element].move_pattern;
8024     int newx, newy;
8025
8026     Moving2Blocked(x, y, &newx, &newy);
8027
8028     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8029       return;
8030
8031     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8032         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8033     {
8034       WasJustMoving[x][y] = 0;
8035       CheckCollision[x][y] = 0;
8036
8037       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8038
8039       if (Tile[x][y] != element)        // element has changed
8040         return;
8041     }
8042
8043     if (!MovDelay[x][y])        // start new movement phase
8044     {
8045       // all objects that can change their move direction after each step
8046       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8047
8048       if (element != EL_YAMYAM &&
8049           element != EL_DARK_YAMYAM &&
8050           element != EL_PACMAN &&
8051           !(move_pattern & MV_ANY_DIRECTION) &&
8052           move_pattern != MV_TURNING_LEFT &&
8053           move_pattern != MV_TURNING_RIGHT &&
8054           move_pattern != MV_TURNING_LEFT_RIGHT &&
8055           move_pattern != MV_TURNING_RIGHT_LEFT &&
8056           move_pattern != MV_TURNING_RANDOM)
8057       {
8058         TurnRound(x, y);
8059
8060         if (MovDelay[x][y] && (element == EL_BUG ||
8061                                element == EL_SPACESHIP ||
8062                                element == EL_SP_SNIKSNAK ||
8063                                element == EL_SP_ELECTRON ||
8064                                element == EL_MOLE))
8065           TEST_DrawLevelField(x, y);
8066       }
8067     }
8068
8069     if (MovDelay[x][y])         // wait some time before next movement
8070     {
8071       MovDelay[x][y]--;
8072
8073       if (element == EL_ROBOT ||
8074           element == EL_YAMYAM ||
8075           element == EL_DARK_YAMYAM)
8076       {
8077         DrawLevelElementAnimationIfNeeded(x, y, element);
8078         PlayLevelSoundAction(x, y, ACTION_WAITING);
8079       }
8080       else if (element == EL_SP_ELECTRON)
8081         DrawLevelElementAnimationIfNeeded(x, y, element);
8082       else if (element == EL_DRAGON)
8083       {
8084         int i;
8085         int dir = MovDir[x][y];
8086         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8087         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8088         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8089                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8090                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8091                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8092         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8093
8094         GfxAction[x][y] = ACTION_ATTACKING;
8095
8096         if (IS_PLAYER(x, y))
8097           DrawPlayerField(x, y);
8098         else
8099           TEST_DrawLevelField(x, y);
8100
8101         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8102
8103         for (i = 1; i <= 3; i++)
8104         {
8105           int xx = x + i * dx;
8106           int yy = y + i * dy;
8107           int sx = SCREENX(xx);
8108           int sy = SCREENY(yy);
8109           int flame_graphic = graphic + (i - 1);
8110
8111           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8112             break;
8113
8114           if (MovDelay[x][y])
8115           {
8116             int flamed = MovingOrBlocked2Element(xx, yy);
8117
8118             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8119               Bang(xx, yy);
8120             else
8121               RemoveMovingField(xx, yy);
8122
8123             ChangeDelay[xx][yy] = 0;
8124
8125             Tile[xx][yy] = EL_FLAMES;
8126
8127             if (IN_SCR_FIELD(sx, sy))
8128             {
8129               TEST_DrawLevelFieldCrumbled(xx, yy);
8130               DrawGraphic(sx, sy, flame_graphic, frame);
8131             }
8132           }
8133           else
8134           {
8135             if (Tile[xx][yy] == EL_FLAMES)
8136               Tile[xx][yy] = EL_EMPTY;
8137             TEST_DrawLevelField(xx, yy);
8138           }
8139         }
8140       }
8141
8142       if (MovDelay[x][y])       // element still has to wait some time
8143       {
8144         PlayLevelSoundAction(x, y, ACTION_WAITING);
8145
8146         return;
8147       }
8148     }
8149
8150     // now make next step
8151
8152     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8153
8154     if (DONT_COLLIDE_WITH(element) &&
8155         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8156         !PLAYER_ENEMY_PROTECTED(newx, newy))
8157     {
8158       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8159
8160       return;
8161     }
8162
8163     else if (CAN_MOVE_INTO_ACID(element) &&
8164              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8165              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8166              (MovDir[x][y] == MV_DOWN ||
8167               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8168     {
8169       SplashAcid(newx, newy);
8170       Store[x][y] = EL_ACID;
8171     }
8172     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8173     {
8174       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8175           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8176           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8177           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8178       {
8179         RemoveField(x, y);
8180         TEST_DrawLevelField(x, y);
8181
8182         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8183         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8184           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8185
8186         game.friends_still_needed--;
8187         if (!game.friends_still_needed &&
8188             !game.GameOver &&
8189             game.all_players_gone)
8190           LevelSolved();
8191
8192         return;
8193       }
8194       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8195       {
8196         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8197           TEST_DrawLevelField(newx, newy);
8198         else
8199           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8200       }
8201       else if (!IS_FREE(newx, newy))
8202       {
8203         GfxAction[x][y] = ACTION_WAITING;
8204
8205         if (IS_PLAYER(x, y))
8206           DrawPlayerField(x, y);
8207         else
8208           TEST_DrawLevelField(x, y);
8209
8210         return;
8211       }
8212     }
8213     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8214     {
8215       if (IS_FOOD_PIG(Tile[newx][newy]))
8216       {
8217         if (IS_MOVING(newx, newy))
8218           RemoveMovingField(newx, newy);
8219         else
8220         {
8221           Tile[newx][newy] = EL_EMPTY;
8222           TEST_DrawLevelField(newx, newy);
8223         }
8224
8225         PlayLevelSound(x, y, SND_PIG_DIGGING);
8226       }
8227       else if (!IS_FREE(newx, newy))
8228       {
8229         if (IS_PLAYER(x, y))
8230           DrawPlayerField(x, y);
8231         else
8232           TEST_DrawLevelField(x, y);
8233
8234         return;
8235       }
8236     }
8237     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8238     {
8239       if (Store[x][y] != EL_EMPTY)
8240       {
8241         boolean can_clone = FALSE;
8242         int xx, yy;
8243
8244         // check if element to clone is still there
8245         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8246         {
8247           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8248           {
8249             can_clone = TRUE;
8250
8251             break;
8252           }
8253         }
8254
8255         // cannot clone or target field not free anymore -- do not clone
8256         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8257           Store[x][y] = EL_EMPTY;
8258       }
8259
8260       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8261       {
8262         if (IS_MV_DIAGONAL(MovDir[x][y]))
8263         {
8264           int diagonal_move_dir = MovDir[x][y];
8265           int stored = Store[x][y];
8266           int change_delay = 8;
8267           int graphic;
8268
8269           // android is moving diagonally
8270
8271           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8272
8273           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8274           GfxElement[x][y] = EL_EMC_ANDROID;
8275           GfxAction[x][y] = ACTION_SHRINKING;
8276           GfxDir[x][y] = diagonal_move_dir;
8277           ChangeDelay[x][y] = change_delay;
8278
8279           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8280                                    GfxDir[x][y]);
8281
8282           DrawLevelGraphicAnimation(x, y, graphic);
8283           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8284
8285           if (Tile[newx][newy] == EL_ACID)
8286           {
8287             SplashAcid(newx, newy);
8288
8289             return;
8290           }
8291
8292           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8293
8294           Store[newx][newy] = EL_EMC_ANDROID;
8295           GfxElement[newx][newy] = EL_EMC_ANDROID;
8296           GfxAction[newx][newy] = ACTION_GROWING;
8297           GfxDir[newx][newy] = diagonal_move_dir;
8298           ChangeDelay[newx][newy] = change_delay;
8299
8300           graphic = el_act_dir2img(GfxElement[newx][newy],
8301                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8302
8303           DrawLevelGraphicAnimation(newx, newy, graphic);
8304           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8305
8306           return;
8307         }
8308         else
8309         {
8310           Tile[newx][newy] = EL_EMPTY;
8311           TEST_DrawLevelField(newx, newy);
8312
8313           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8314         }
8315       }
8316       else if (!IS_FREE(newx, newy))
8317       {
8318         return;
8319       }
8320     }
8321     else if (IS_CUSTOM_ELEMENT(element) &&
8322              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8323     {
8324       if (!DigFieldByCE(newx, newy, element))
8325         return;
8326
8327       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8328       {
8329         RunnerVisit[x][y] = FrameCounter;
8330         PlayerVisit[x][y] /= 8;         // expire player visit path
8331       }
8332     }
8333     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8334     {
8335       if (!IS_FREE(newx, newy))
8336       {
8337         if (IS_PLAYER(x, y))
8338           DrawPlayerField(x, y);
8339         else
8340           TEST_DrawLevelField(x, y);
8341
8342         return;
8343       }
8344       else
8345       {
8346         boolean wanna_flame = !RND(10);
8347         int dx = newx - x, dy = newy - y;
8348         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8349         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8350         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8351                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8352         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8353                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8354
8355         if ((wanna_flame ||
8356              IS_CLASSIC_ENEMY(element1) ||
8357              IS_CLASSIC_ENEMY(element2)) &&
8358             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8359             element1 != EL_FLAMES && element2 != EL_FLAMES)
8360         {
8361           ResetGfxAnimation(x, y);
8362           GfxAction[x][y] = ACTION_ATTACKING;
8363
8364           if (IS_PLAYER(x, y))
8365             DrawPlayerField(x, y);
8366           else
8367             TEST_DrawLevelField(x, y);
8368
8369           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8370
8371           MovDelay[x][y] = 50;
8372
8373           Tile[newx][newy] = EL_FLAMES;
8374           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8375             Tile[newx1][newy1] = EL_FLAMES;
8376           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8377             Tile[newx2][newy2] = EL_FLAMES;
8378
8379           return;
8380         }
8381       }
8382     }
8383     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8384              Tile[newx][newy] == EL_DIAMOND)
8385     {
8386       if (IS_MOVING(newx, newy))
8387         RemoveMovingField(newx, newy);
8388       else
8389       {
8390         Tile[newx][newy] = EL_EMPTY;
8391         TEST_DrawLevelField(newx, newy);
8392       }
8393
8394       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8395     }
8396     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8397              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8398     {
8399       if (AmoebaNr[newx][newy])
8400       {
8401         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8402         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8403             Tile[newx][newy] == EL_BD_AMOEBA)
8404           AmoebaCnt[AmoebaNr[newx][newy]]--;
8405       }
8406
8407       if (IS_MOVING(newx, newy))
8408       {
8409         RemoveMovingField(newx, newy);
8410       }
8411       else
8412       {
8413         Tile[newx][newy] = EL_EMPTY;
8414         TEST_DrawLevelField(newx, newy);
8415       }
8416
8417       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8418     }
8419     else if ((element == EL_PACMAN || element == EL_MOLE)
8420              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8421     {
8422       if (AmoebaNr[newx][newy])
8423       {
8424         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8425         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8426             Tile[newx][newy] == EL_BD_AMOEBA)
8427           AmoebaCnt[AmoebaNr[newx][newy]]--;
8428       }
8429
8430       if (element == EL_MOLE)
8431       {
8432         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8433         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8434
8435         ResetGfxAnimation(x, y);
8436         GfxAction[x][y] = ACTION_DIGGING;
8437         TEST_DrawLevelField(x, y);
8438
8439         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8440
8441         return;                         // wait for shrinking amoeba
8442       }
8443       else      // element == EL_PACMAN
8444       {
8445         Tile[newx][newy] = EL_EMPTY;
8446         TEST_DrawLevelField(newx, newy);
8447         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8448       }
8449     }
8450     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8451              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8452               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8453     {
8454       // wait for shrinking amoeba to completely disappear
8455       return;
8456     }
8457     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8458     {
8459       // object was running against a wall
8460
8461       TurnRound(x, y);
8462
8463       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8464         DrawLevelElementAnimation(x, y, element);
8465
8466       if (DONT_TOUCH(element))
8467         TestIfBadThingTouchesPlayer(x, y);
8468
8469       return;
8470     }
8471
8472     InitMovingField(x, y, MovDir[x][y]);
8473
8474     PlayLevelSoundAction(x, y, ACTION_MOVING);
8475   }
8476
8477   if (MovDir[x][y])
8478     ContinueMoving(x, y);
8479 }
8480
8481 void ContinueMoving(int x, int y)
8482 {
8483   int element = Tile[x][y];
8484   struct ElementInfo *ei = &element_info[element];
8485   int direction = MovDir[x][y];
8486   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8487   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8488   int newx = x + dx, newy = y + dy;
8489   int stored = Store[x][y];
8490   int stored_new = Store[newx][newy];
8491   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8492   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8493   boolean last_line = (newy == lev_fieldy - 1);
8494
8495   MovPos[x][y] += getElementMoveStepsize(x, y);
8496
8497   if (pushed_by_player) // special case: moving object pushed by player
8498     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8499
8500   if (ABS(MovPos[x][y]) < TILEX)
8501   {
8502     TEST_DrawLevelField(x, y);
8503
8504     return;     // element is still moving
8505   }
8506
8507   // element reached destination field
8508
8509   Tile[x][y] = EL_EMPTY;
8510   Tile[newx][newy] = element;
8511   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8512
8513   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8514   {
8515     element = Tile[newx][newy] = EL_ACID;
8516   }
8517   else if (element == EL_MOLE)
8518   {
8519     Tile[x][y] = EL_SAND;
8520
8521     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8522   }
8523   else if (element == EL_QUICKSAND_FILLING)
8524   {
8525     element = Tile[newx][newy] = get_next_element(element);
8526     Store[newx][newy] = Store[x][y];
8527   }
8528   else if (element == EL_QUICKSAND_EMPTYING)
8529   {
8530     Tile[x][y] = get_next_element(element);
8531     element = Tile[newx][newy] = Store[x][y];
8532   }
8533   else if (element == EL_QUICKSAND_FAST_FILLING)
8534   {
8535     element = Tile[newx][newy] = get_next_element(element);
8536     Store[newx][newy] = Store[x][y];
8537   }
8538   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8539   {
8540     Tile[x][y] = get_next_element(element);
8541     element = Tile[newx][newy] = Store[x][y];
8542   }
8543   else if (element == EL_MAGIC_WALL_FILLING)
8544   {
8545     element = Tile[newx][newy] = get_next_element(element);
8546     if (!game.magic_wall_active)
8547       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8548     Store[newx][newy] = Store[x][y];
8549   }
8550   else if (element == EL_MAGIC_WALL_EMPTYING)
8551   {
8552     Tile[x][y] = get_next_element(element);
8553     if (!game.magic_wall_active)
8554       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8555     element = Tile[newx][newy] = Store[x][y];
8556
8557     InitField(newx, newy, FALSE);
8558   }
8559   else if (element == EL_BD_MAGIC_WALL_FILLING)
8560   {
8561     element = Tile[newx][newy] = get_next_element(element);
8562     if (!game.magic_wall_active)
8563       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8564     Store[newx][newy] = Store[x][y];
8565   }
8566   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8567   {
8568     Tile[x][y] = get_next_element(element);
8569     if (!game.magic_wall_active)
8570       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8571     element = Tile[newx][newy] = Store[x][y];
8572
8573     InitField(newx, newy, FALSE);
8574   }
8575   else if (element == EL_DC_MAGIC_WALL_FILLING)
8576   {
8577     element = Tile[newx][newy] = get_next_element(element);
8578     if (!game.magic_wall_active)
8579       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8580     Store[newx][newy] = Store[x][y];
8581   }
8582   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8583   {
8584     Tile[x][y] = get_next_element(element);
8585     if (!game.magic_wall_active)
8586       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8587     element = Tile[newx][newy] = Store[x][y];
8588
8589     InitField(newx, newy, FALSE);
8590   }
8591   else if (element == EL_AMOEBA_DROPPING)
8592   {
8593     Tile[x][y] = get_next_element(element);
8594     element = Tile[newx][newy] = Store[x][y];
8595   }
8596   else if (element == EL_SOKOBAN_OBJECT)
8597   {
8598     if (Back[x][y])
8599       Tile[x][y] = Back[x][y];
8600
8601     if (Back[newx][newy])
8602       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8603
8604     Back[x][y] = Back[newx][newy] = 0;
8605   }
8606
8607   Store[x][y] = EL_EMPTY;
8608   MovPos[x][y] = 0;
8609   MovDir[x][y] = 0;
8610   MovDelay[x][y] = 0;
8611
8612   MovDelay[newx][newy] = 0;
8613
8614   if (CAN_CHANGE_OR_HAS_ACTION(element))
8615   {
8616     // copy element change control values to new field
8617     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8618     ChangePage[newx][newy]  = ChangePage[x][y];
8619     ChangeCount[newx][newy] = ChangeCount[x][y];
8620     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8621   }
8622
8623   CustomValue[newx][newy] = CustomValue[x][y];
8624
8625   ChangeDelay[x][y] = 0;
8626   ChangePage[x][y] = -1;
8627   ChangeCount[x][y] = 0;
8628   ChangeEvent[x][y] = -1;
8629
8630   CustomValue[x][y] = 0;
8631
8632   // copy animation control values to new field
8633   GfxFrame[newx][newy]  = GfxFrame[x][y];
8634   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8635   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8636   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8637
8638   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8639
8640   // some elements can leave other elements behind after moving
8641   if (ei->move_leave_element != EL_EMPTY &&
8642       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8643       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8644   {
8645     int move_leave_element = ei->move_leave_element;
8646
8647     // this makes it possible to leave the removed element again
8648     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8649       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8650
8651     Tile[x][y] = move_leave_element;
8652
8653     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8654       MovDir[x][y] = direction;
8655
8656     InitField(x, y, FALSE);
8657
8658     if (GFX_CRUMBLED(Tile[x][y]))
8659       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8660
8661     if (ELEM_IS_PLAYER(move_leave_element))
8662       RelocatePlayer(x, y, move_leave_element);
8663   }
8664
8665   // do this after checking for left-behind element
8666   ResetGfxAnimation(x, y);      // reset animation values for old field
8667
8668   if (!CAN_MOVE(element) ||
8669       (CAN_FALL(element) && direction == MV_DOWN &&
8670        (element == EL_SPRING ||
8671         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8672         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8673     GfxDir[x][y] = MovDir[newx][newy] = 0;
8674
8675   TEST_DrawLevelField(x, y);
8676   TEST_DrawLevelField(newx, newy);
8677
8678   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8679
8680   // prevent pushed element from moving on in pushed direction
8681   if (pushed_by_player && CAN_MOVE(element) &&
8682       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8683       !(element_info[element].move_pattern & direction))
8684     TurnRound(newx, newy);
8685
8686   // prevent elements on conveyor belt from moving on in last direction
8687   if (pushed_by_conveyor && CAN_FALL(element) &&
8688       direction & MV_HORIZONTAL)
8689     MovDir[newx][newy] = 0;
8690
8691   if (!pushed_by_player)
8692   {
8693     int nextx = newx + dx, nexty = newy + dy;
8694     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8695
8696     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8697
8698     if (CAN_FALL(element) && direction == MV_DOWN)
8699       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8700
8701     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8702       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8703
8704     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8705       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8706   }
8707
8708   if (DONT_TOUCH(element))      // object may be nasty to player or others
8709   {
8710     TestIfBadThingTouchesPlayer(newx, newy);
8711     TestIfBadThingTouchesFriend(newx, newy);
8712
8713     if (!IS_CUSTOM_ELEMENT(element))
8714       TestIfBadThingTouchesOtherBadThing(newx, newy);
8715   }
8716   else if (element == EL_PENGUIN)
8717     TestIfFriendTouchesBadThing(newx, newy);
8718
8719   if (DONT_GET_HIT_BY(element))
8720   {
8721     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8722   }
8723
8724   // give the player one last chance (one more frame) to move away
8725   if (CAN_FALL(element) && direction == MV_DOWN &&
8726       (last_line || (!IS_FREE(x, newy + 1) &&
8727                      (!IS_PLAYER(x, newy + 1) ||
8728                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8729     Impact(x, newy);
8730
8731   if (pushed_by_player && !game.use_change_when_pushing_bug)
8732   {
8733     int push_side = MV_DIR_OPPOSITE(direction);
8734     struct PlayerInfo *player = PLAYERINFO(x, y);
8735
8736     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8737                                player->index_bit, push_side);
8738     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8739                                         player->index_bit, push_side);
8740   }
8741
8742   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8743     MovDelay[newx][newy] = 1;
8744
8745   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8746
8747   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8748   TestIfElementHitsCustomElement(newx, newy, direction);
8749   TestIfPlayerTouchesCustomElement(newx, newy);
8750   TestIfElementTouchesCustomElement(newx, newy);
8751
8752   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8753       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8754     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8755                              MV_DIR_OPPOSITE(direction));
8756 }
8757
8758 int AmoebaNeighbourNr(int ax, int ay)
8759 {
8760   int i;
8761   int element = Tile[ax][ay];
8762   int group_nr = 0;
8763   static int xy[4][2] =
8764   {
8765     { 0, -1 },
8766     { -1, 0 },
8767     { +1, 0 },
8768     { 0, +1 }
8769   };
8770
8771   for (i = 0; i < NUM_DIRECTIONS; i++)
8772   {
8773     int x = ax + xy[i][0];
8774     int y = ay + xy[i][1];
8775
8776     if (!IN_LEV_FIELD(x, y))
8777       continue;
8778
8779     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8780       group_nr = AmoebaNr[x][y];
8781   }
8782
8783   return group_nr;
8784 }
8785
8786 static void AmoebaMerge(int ax, int ay)
8787 {
8788   int i, x, y, xx, yy;
8789   int new_group_nr = AmoebaNr[ax][ay];
8790   static int xy[4][2] =
8791   {
8792     { 0, -1 },
8793     { -1, 0 },
8794     { +1, 0 },
8795     { 0, +1 }
8796   };
8797
8798   if (new_group_nr == 0)
8799     return;
8800
8801   for (i = 0; i < NUM_DIRECTIONS; i++)
8802   {
8803     x = ax + xy[i][0];
8804     y = ay + xy[i][1];
8805
8806     if (!IN_LEV_FIELD(x, y))
8807       continue;
8808
8809     if ((Tile[x][y] == EL_AMOEBA_FULL ||
8810          Tile[x][y] == EL_BD_AMOEBA ||
8811          Tile[x][y] == EL_AMOEBA_DEAD) &&
8812         AmoebaNr[x][y] != new_group_nr)
8813     {
8814       int old_group_nr = AmoebaNr[x][y];
8815
8816       if (old_group_nr == 0)
8817         return;
8818
8819       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8820       AmoebaCnt[old_group_nr] = 0;
8821       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8822       AmoebaCnt2[old_group_nr] = 0;
8823
8824       SCAN_PLAYFIELD(xx, yy)
8825       {
8826         if (AmoebaNr[xx][yy] == old_group_nr)
8827           AmoebaNr[xx][yy] = new_group_nr;
8828       }
8829     }
8830   }
8831 }
8832
8833 void AmoebaToDiamond(int ax, int ay)
8834 {
8835   int i, x, y;
8836
8837   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
8838   {
8839     int group_nr = AmoebaNr[ax][ay];
8840
8841 #ifdef DEBUG
8842     if (group_nr == 0)
8843     {
8844       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
8845       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
8846
8847       return;
8848     }
8849 #endif
8850
8851     SCAN_PLAYFIELD(x, y)
8852     {
8853       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8854       {
8855         AmoebaNr[x][y] = 0;
8856         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
8857       }
8858     }
8859
8860     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8861                             SND_AMOEBA_TURNING_TO_GEM :
8862                             SND_AMOEBA_TURNING_TO_ROCK));
8863     Bang(ax, ay);
8864   }
8865   else
8866   {
8867     static int xy[4][2] =
8868     {
8869       { 0, -1 },
8870       { -1, 0 },
8871       { +1, 0 },
8872       { 0, +1 }
8873     };
8874
8875     for (i = 0; i < NUM_DIRECTIONS; i++)
8876     {
8877       x = ax + xy[i][0];
8878       y = ay + xy[i][1];
8879
8880       if (!IN_LEV_FIELD(x, y))
8881         continue;
8882
8883       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
8884       {
8885         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8886                               SND_AMOEBA_TURNING_TO_GEM :
8887                               SND_AMOEBA_TURNING_TO_ROCK));
8888         Bang(x, y);
8889       }
8890     }
8891   }
8892 }
8893
8894 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
8895 {
8896   int x, y;
8897   int group_nr = AmoebaNr[ax][ay];
8898   boolean done = FALSE;
8899
8900 #ifdef DEBUG
8901   if (group_nr == 0)
8902   {
8903     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
8904     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
8905
8906     return;
8907   }
8908 #endif
8909
8910   SCAN_PLAYFIELD(x, y)
8911   {
8912     if (AmoebaNr[x][y] == group_nr &&
8913         (Tile[x][y] == EL_AMOEBA_DEAD ||
8914          Tile[x][y] == EL_BD_AMOEBA ||
8915          Tile[x][y] == EL_AMOEBA_GROWING))
8916     {
8917       AmoebaNr[x][y] = 0;
8918       Tile[x][y] = new_element;
8919       InitField(x, y, FALSE);
8920       TEST_DrawLevelField(x, y);
8921       done = TRUE;
8922     }
8923   }
8924
8925   if (done)
8926     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8927                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8928                             SND_BD_AMOEBA_TURNING_TO_GEM));
8929 }
8930
8931 static void AmoebaGrowing(int x, int y)
8932 {
8933   static unsigned int sound_delay = 0;
8934   static unsigned int sound_delay_value = 0;
8935
8936   if (!MovDelay[x][y])          // start new growing cycle
8937   {
8938     MovDelay[x][y] = 7;
8939
8940     if (DelayReached(&sound_delay, sound_delay_value))
8941     {
8942       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8943       sound_delay_value = 30;
8944     }
8945   }
8946
8947   if (MovDelay[x][y])           // wait some time before growing bigger
8948   {
8949     MovDelay[x][y]--;
8950     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8951     {
8952       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8953                                            6 - MovDelay[x][y]);
8954
8955       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8956     }
8957
8958     if (!MovDelay[x][y])
8959     {
8960       Tile[x][y] = Store[x][y];
8961       Store[x][y] = 0;
8962       TEST_DrawLevelField(x, y);
8963     }
8964   }
8965 }
8966
8967 static void AmoebaShrinking(int x, int y)
8968 {
8969   static unsigned int sound_delay = 0;
8970   static unsigned int sound_delay_value = 0;
8971
8972   if (!MovDelay[x][y])          // start new shrinking cycle
8973   {
8974     MovDelay[x][y] = 7;
8975
8976     if (DelayReached(&sound_delay, sound_delay_value))
8977       sound_delay_value = 30;
8978   }
8979
8980   if (MovDelay[x][y])           // wait some time before shrinking
8981   {
8982     MovDelay[x][y]--;
8983     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8984     {
8985       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8986                                            6 - MovDelay[x][y]);
8987
8988       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8989     }
8990
8991     if (!MovDelay[x][y])
8992     {
8993       Tile[x][y] = EL_EMPTY;
8994       TEST_DrawLevelField(x, y);
8995
8996       // don't let mole enter this field in this cycle;
8997       // (give priority to objects falling to this field from above)
8998       Stop[x][y] = TRUE;
8999     }
9000   }
9001 }
9002
9003 static void AmoebaReproduce(int ax, int ay)
9004 {
9005   int i;
9006   int element = Tile[ax][ay];
9007   int graphic = el2img(element);
9008   int newax = ax, neway = ay;
9009   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9010   static int xy[4][2] =
9011   {
9012     { 0, -1 },
9013     { -1, 0 },
9014     { +1, 0 },
9015     { 0, +1 }
9016   };
9017
9018   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9019   {
9020     Tile[ax][ay] = EL_AMOEBA_DEAD;
9021     TEST_DrawLevelField(ax, ay);
9022     return;
9023   }
9024
9025   if (IS_ANIMATED(graphic))
9026     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9027
9028   if (!MovDelay[ax][ay])        // start making new amoeba field
9029     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9030
9031   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9032   {
9033     MovDelay[ax][ay]--;
9034     if (MovDelay[ax][ay])
9035       return;
9036   }
9037
9038   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9039   {
9040     int start = RND(4);
9041     int x = ax + xy[start][0];
9042     int y = ay + xy[start][1];
9043
9044     if (!IN_LEV_FIELD(x, y))
9045       return;
9046
9047     if (IS_FREE(x, y) ||
9048         CAN_GROW_INTO(Tile[x][y]) ||
9049         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9050         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9051     {
9052       newax = x;
9053       neway = y;
9054     }
9055
9056     if (newax == ax && neway == ay)
9057       return;
9058   }
9059   else                          // normal or "filled" (BD style) amoeba
9060   {
9061     int start = RND(4);
9062     boolean waiting_for_player = FALSE;
9063
9064     for (i = 0; i < NUM_DIRECTIONS; i++)
9065     {
9066       int j = (start + i) % 4;
9067       int x = ax + xy[j][0];
9068       int y = ay + xy[j][1];
9069
9070       if (!IN_LEV_FIELD(x, y))
9071         continue;
9072
9073       if (IS_FREE(x, y) ||
9074           CAN_GROW_INTO(Tile[x][y]) ||
9075           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9076           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9077       {
9078         newax = x;
9079         neway = y;
9080         break;
9081       }
9082       else if (IS_PLAYER(x, y))
9083         waiting_for_player = TRUE;
9084     }
9085
9086     if (newax == ax && neway == ay)             // amoeba cannot grow
9087     {
9088       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9089       {
9090         Tile[ax][ay] = EL_AMOEBA_DEAD;
9091         TEST_DrawLevelField(ax, ay);
9092         AmoebaCnt[AmoebaNr[ax][ay]]--;
9093
9094         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9095         {
9096           if (element == EL_AMOEBA_FULL)
9097             AmoebaToDiamond(ax, ay);
9098           else if (element == EL_BD_AMOEBA)
9099             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9100         }
9101       }
9102       return;
9103     }
9104     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9105     {
9106       // amoeba gets larger by growing in some direction
9107
9108       int new_group_nr = AmoebaNr[ax][ay];
9109
9110 #ifdef DEBUG
9111   if (new_group_nr == 0)
9112   {
9113     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9114           newax, neway);
9115     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9116
9117     return;
9118   }
9119 #endif
9120
9121       AmoebaNr[newax][neway] = new_group_nr;
9122       AmoebaCnt[new_group_nr]++;
9123       AmoebaCnt2[new_group_nr]++;
9124
9125       // if amoeba touches other amoeba(s) after growing, unify them
9126       AmoebaMerge(newax, neway);
9127
9128       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9129       {
9130         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9131         return;
9132       }
9133     }
9134   }
9135
9136   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9137       (neway == lev_fieldy - 1 && newax != ax))
9138   {
9139     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9140     Store[newax][neway] = element;
9141   }
9142   else if (neway == ay || element == EL_EMC_DRIPPER)
9143   {
9144     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9145
9146     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9147   }
9148   else
9149   {
9150     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9151     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9152     Store[ax][ay] = EL_AMOEBA_DROP;
9153     ContinueMoving(ax, ay);
9154     return;
9155   }
9156
9157   TEST_DrawLevelField(newax, neway);
9158 }
9159
9160 static void Life(int ax, int ay)
9161 {
9162   int x1, y1, x2, y2;
9163   int life_time = 40;
9164   int element = Tile[ax][ay];
9165   int graphic = el2img(element);
9166   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9167                          level.biomaze);
9168   boolean changed = FALSE;
9169
9170   if (IS_ANIMATED(graphic))
9171     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9172
9173   if (Stop[ax][ay])
9174     return;
9175
9176   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9177     MovDelay[ax][ay] = life_time;
9178
9179   if (MovDelay[ax][ay])         // wait some time before next cycle
9180   {
9181     MovDelay[ax][ay]--;
9182     if (MovDelay[ax][ay])
9183       return;
9184   }
9185
9186   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9187   {
9188     int xx = ax+x1, yy = ay+y1;
9189     int old_element = Tile[xx][yy];
9190     int num_neighbours = 0;
9191
9192     if (!IN_LEV_FIELD(xx, yy))
9193       continue;
9194
9195     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9196     {
9197       int x = xx+x2, y = yy+y2;
9198
9199       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9200         continue;
9201
9202       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9203       boolean is_neighbour = FALSE;
9204
9205       if (level.use_life_bugs)
9206         is_neighbour =
9207           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9208            (IS_FREE(x, y)                             &&  Stop[x][y]));
9209       else
9210         is_neighbour =
9211           (Last[x][y] == element || is_player_cell);
9212
9213       if (is_neighbour)
9214         num_neighbours++;
9215     }
9216
9217     boolean is_free = FALSE;
9218
9219     if (level.use_life_bugs)
9220       is_free = (IS_FREE(xx, yy));
9221     else
9222       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9223
9224     if (xx == ax && yy == ay)           // field in the middle
9225     {
9226       if (num_neighbours < life_parameter[0] ||
9227           num_neighbours > life_parameter[1])
9228       {
9229         Tile[xx][yy] = EL_EMPTY;
9230         if (Tile[xx][yy] != old_element)
9231           TEST_DrawLevelField(xx, yy);
9232         Stop[xx][yy] = TRUE;
9233         changed = TRUE;
9234       }
9235     }
9236     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9237     {                                   // free border field
9238       if (num_neighbours >= life_parameter[2] &&
9239           num_neighbours <= life_parameter[3])
9240       {
9241         Tile[xx][yy] = element;
9242         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9243         if (Tile[xx][yy] != old_element)
9244           TEST_DrawLevelField(xx, yy);
9245         Stop[xx][yy] = TRUE;
9246         changed = TRUE;
9247       }
9248     }
9249   }
9250
9251   if (changed)
9252     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9253                    SND_GAME_OF_LIFE_GROWING);
9254 }
9255
9256 static void InitRobotWheel(int x, int y)
9257 {
9258   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9259 }
9260
9261 static void RunRobotWheel(int x, int y)
9262 {
9263   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9264 }
9265
9266 static void StopRobotWheel(int x, int y)
9267 {
9268   if (game.robot_wheel_x == x &&
9269       game.robot_wheel_y == y)
9270   {
9271     game.robot_wheel_x = -1;
9272     game.robot_wheel_y = -1;
9273     game.robot_wheel_active = FALSE;
9274   }
9275 }
9276
9277 static void InitTimegateWheel(int x, int y)
9278 {
9279   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9280 }
9281
9282 static void RunTimegateWheel(int x, int y)
9283 {
9284   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9285 }
9286
9287 static void InitMagicBallDelay(int x, int y)
9288 {
9289   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9290 }
9291
9292 static void ActivateMagicBall(int bx, int by)
9293 {
9294   int x, y;
9295
9296   if (level.ball_random)
9297   {
9298     int pos_border = RND(8);    // select one of the eight border elements
9299     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9300     int xx = pos_content % 3;
9301     int yy = pos_content / 3;
9302
9303     x = bx - 1 + xx;
9304     y = by - 1 + yy;
9305
9306     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9307       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9308   }
9309   else
9310   {
9311     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9312     {
9313       int xx = x - bx + 1;
9314       int yy = y - by + 1;
9315
9316       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9317         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9318     }
9319   }
9320
9321   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9322 }
9323
9324 static void CheckExit(int x, int y)
9325 {
9326   if (game.gems_still_needed > 0 ||
9327       game.sokoban_fields_still_needed > 0 ||
9328       game.sokoban_objects_still_needed > 0 ||
9329       game.lights_still_needed > 0)
9330   {
9331     int element = Tile[x][y];
9332     int graphic = el2img(element);
9333
9334     if (IS_ANIMATED(graphic))
9335       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9336
9337     return;
9338   }
9339
9340   // do not re-open exit door closed after last player
9341   if (game.all_players_gone)
9342     return;
9343
9344   Tile[x][y] = EL_EXIT_OPENING;
9345
9346   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9347 }
9348
9349 static void CheckExitEM(int x, int y)
9350 {
9351   if (game.gems_still_needed > 0 ||
9352       game.sokoban_fields_still_needed > 0 ||
9353       game.sokoban_objects_still_needed > 0 ||
9354       game.lights_still_needed > 0)
9355   {
9356     int element = Tile[x][y];
9357     int graphic = el2img(element);
9358
9359     if (IS_ANIMATED(graphic))
9360       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9361
9362     return;
9363   }
9364
9365   // do not re-open exit door closed after last player
9366   if (game.all_players_gone)
9367     return;
9368
9369   Tile[x][y] = EL_EM_EXIT_OPENING;
9370
9371   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9372 }
9373
9374 static void CheckExitSteel(int x, int y)
9375 {
9376   if (game.gems_still_needed > 0 ||
9377       game.sokoban_fields_still_needed > 0 ||
9378       game.sokoban_objects_still_needed > 0 ||
9379       game.lights_still_needed > 0)
9380   {
9381     int element = Tile[x][y];
9382     int graphic = el2img(element);
9383
9384     if (IS_ANIMATED(graphic))
9385       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9386
9387     return;
9388   }
9389
9390   // do not re-open exit door closed after last player
9391   if (game.all_players_gone)
9392     return;
9393
9394   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9395
9396   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9397 }
9398
9399 static void CheckExitSteelEM(int x, int y)
9400 {
9401   if (game.gems_still_needed > 0 ||
9402       game.sokoban_fields_still_needed > 0 ||
9403       game.sokoban_objects_still_needed > 0 ||
9404       game.lights_still_needed > 0)
9405   {
9406     int element = Tile[x][y];
9407     int graphic = el2img(element);
9408
9409     if (IS_ANIMATED(graphic))
9410       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9411
9412     return;
9413   }
9414
9415   // do not re-open exit door closed after last player
9416   if (game.all_players_gone)
9417     return;
9418
9419   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9420
9421   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9422 }
9423
9424 static void CheckExitSP(int x, int y)
9425 {
9426   if (game.gems_still_needed > 0)
9427   {
9428     int element = Tile[x][y];
9429     int graphic = el2img(element);
9430
9431     if (IS_ANIMATED(graphic))
9432       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9433
9434     return;
9435   }
9436
9437   // do not re-open exit door closed after last player
9438   if (game.all_players_gone)
9439     return;
9440
9441   Tile[x][y] = EL_SP_EXIT_OPENING;
9442
9443   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9444 }
9445
9446 static void CloseAllOpenTimegates(void)
9447 {
9448   int x, y;
9449
9450   SCAN_PLAYFIELD(x, y)
9451   {
9452     int element = Tile[x][y];
9453
9454     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9455     {
9456       Tile[x][y] = EL_TIMEGATE_CLOSING;
9457
9458       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9459     }
9460   }
9461 }
9462
9463 static void DrawTwinkleOnField(int x, int y)
9464 {
9465   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9466     return;
9467
9468   if (Tile[x][y] == EL_BD_DIAMOND)
9469     return;
9470
9471   if (MovDelay[x][y] == 0)      // next animation frame
9472     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9473
9474   if (MovDelay[x][y] != 0)      // wait some time before next frame
9475   {
9476     MovDelay[x][y]--;
9477
9478     DrawLevelElementAnimation(x, y, Tile[x][y]);
9479
9480     if (MovDelay[x][y] != 0)
9481     {
9482       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9483                                            10 - MovDelay[x][y]);
9484
9485       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9486     }
9487   }
9488 }
9489
9490 static void MauerWaechst(int x, int y)
9491 {
9492   int delay = 6;
9493
9494   if (!MovDelay[x][y])          // next animation frame
9495     MovDelay[x][y] = 3 * delay;
9496
9497   if (MovDelay[x][y])           // wait some time before next frame
9498   {
9499     MovDelay[x][y]--;
9500
9501     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9502     {
9503       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9504       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9505
9506       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9507     }
9508
9509     if (!MovDelay[x][y])
9510     {
9511       if (MovDir[x][y] == MV_LEFT)
9512       {
9513         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9514           TEST_DrawLevelField(x - 1, y);
9515       }
9516       else if (MovDir[x][y] == MV_RIGHT)
9517       {
9518         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9519           TEST_DrawLevelField(x + 1, y);
9520       }
9521       else if (MovDir[x][y] == MV_UP)
9522       {
9523         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9524           TEST_DrawLevelField(x, y - 1);
9525       }
9526       else
9527       {
9528         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9529           TEST_DrawLevelField(x, y + 1);
9530       }
9531
9532       Tile[x][y] = Store[x][y];
9533       Store[x][y] = 0;
9534       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9535       TEST_DrawLevelField(x, y);
9536     }
9537   }
9538 }
9539
9540 static void MauerAbleger(int ax, int ay)
9541 {
9542   int element = Tile[ax][ay];
9543   int graphic = el2img(element);
9544   boolean oben_frei = FALSE, unten_frei = FALSE;
9545   boolean links_frei = FALSE, rechts_frei = FALSE;
9546   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9547   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9548   boolean new_wall = FALSE;
9549
9550   if (IS_ANIMATED(graphic))
9551     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9552
9553   if (!MovDelay[ax][ay])        // start building new wall
9554     MovDelay[ax][ay] = 6;
9555
9556   if (MovDelay[ax][ay])         // wait some time before building new wall
9557   {
9558     MovDelay[ax][ay]--;
9559     if (MovDelay[ax][ay])
9560       return;
9561   }
9562
9563   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9564     oben_frei = TRUE;
9565   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9566     unten_frei = TRUE;
9567   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9568     links_frei = TRUE;
9569   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9570     rechts_frei = TRUE;
9571
9572   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9573       element == EL_EXPANDABLE_WALL_ANY)
9574   {
9575     if (oben_frei)
9576     {
9577       Tile[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9578       Store[ax][ay-1] = element;
9579       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9580       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9581         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9582                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9583       new_wall = TRUE;
9584     }
9585     if (unten_frei)
9586     {
9587       Tile[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9588       Store[ax][ay+1] = element;
9589       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9590       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9591         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9592                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9593       new_wall = TRUE;
9594     }
9595   }
9596
9597   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9598       element == EL_EXPANDABLE_WALL_ANY ||
9599       element == EL_EXPANDABLE_WALL ||
9600       element == EL_BD_EXPANDABLE_WALL)
9601   {
9602     if (links_frei)
9603     {
9604       Tile[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9605       Store[ax-1][ay] = element;
9606       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9607       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9608         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9609                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9610       new_wall = TRUE;
9611     }
9612
9613     if (rechts_frei)
9614     {
9615       Tile[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9616       Store[ax+1][ay] = element;
9617       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9618       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9619         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9620                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9621       new_wall = TRUE;
9622     }
9623   }
9624
9625   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9626     TEST_DrawLevelField(ax, ay);
9627
9628   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9629     oben_massiv = TRUE;
9630   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9631     unten_massiv = TRUE;
9632   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9633     links_massiv = TRUE;
9634   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9635     rechts_massiv = TRUE;
9636
9637   if (((oben_massiv && unten_massiv) ||
9638        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9639        element == EL_EXPANDABLE_WALL) &&
9640       ((links_massiv && rechts_massiv) ||
9641        element == EL_EXPANDABLE_WALL_VERTICAL))
9642     Tile[ax][ay] = EL_WALL;
9643
9644   if (new_wall)
9645     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9646 }
9647
9648 static void MauerAblegerStahl(int ax, int ay)
9649 {
9650   int element = Tile[ax][ay];
9651   int graphic = el2img(element);
9652   boolean oben_frei = FALSE, unten_frei = FALSE;
9653   boolean links_frei = FALSE, rechts_frei = FALSE;
9654   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9655   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9656   boolean new_wall = FALSE;
9657
9658   if (IS_ANIMATED(graphic))
9659     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9660
9661   if (!MovDelay[ax][ay])        // start building new wall
9662     MovDelay[ax][ay] = 6;
9663
9664   if (MovDelay[ax][ay])         // wait some time before building new wall
9665   {
9666     MovDelay[ax][ay]--;
9667     if (MovDelay[ax][ay])
9668       return;
9669   }
9670
9671   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9672     oben_frei = TRUE;
9673   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9674     unten_frei = TRUE;
9675   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9676     links_frei = TRUE;
9677   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9678     rechts_frei = TRUE;
9679
9680   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9681       element == EL_EXPANDABLE_STEELWALL_ANY)
9682   {
9683     if (oben_frei)
9684     {
9685       Tile[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9686       Store[ax][ay-1] = element;
9687       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9688       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9689         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9690                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9691       new_wall = TRUE;
9692     }
9693     if (unten_frei)
9694     {
9695       Tile[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9696       Store[ax][ay+1] = element;
9697       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9698       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9699         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9700                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9701       new_wall = TRUE;
9702     }
9703   }
9704
9705   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9706       element == EL_EXPANDABLE_STEELWALL_ANY)
9707   {
9708     if (links_frei)
9709     {
9710       Tile[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9711       Store[ax-1][ay] = element;
9712       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9713       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9714         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9715                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9716       new_wall = TRUE;
9717     }
9718
9719     if (rechts_frei)
9720     {
9721       Tile[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9722       Store[ax+1][ay] = element;
9723       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9724       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9725         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9726                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9727       new_wall = TRUE;
9728     }
9729   }
9730
9731   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9732     oben_massiv = TRUE;
9733   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9734     unten_massiv = TRUE;
9735   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9736     links_massiv = TRUE;
9737   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9738     rechts_massiv = TRUE;
9739
9740   if (((oben_massiv && unten_massiv) ||
9741        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9742       ((links_massiv && rechts_massiv) ||
9743        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9744     Tile[ax][ay] = EL_STEELWALL;
9745
9746   if (new_wall)
9747     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9748 }
9749
9750 static void CheckForDragon(int x, int y)
9751 {
9752   int i, j;
9753   boolean dragon_found = FALSE;
9754   static int xy[4][2] =
9755   {
9756     { 0, -1 },
9757     { -1, 0 },
9758     { +1, 0 },
9759     { 0, +1 }
9760   };
9761
9762   for (i = 0; i < NUM_DIRECTIONS; i++)
9763   {
9764     for (j = 0; j < 4; j++)
9765     {
9766       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9767
9768       if (IN_LEV_FIELD(xx, yy) &&
9769           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9770       {
9771         if (Tile[xx][yy] == EL_DRAGON)
9772           dragon_found = TRUE;
9773       }
9774       else
9775         break;
9776     }
9777   }
9778
9779   if (!dragon_found)
9780   {
9781     for (i = 0; i < NUM_DIRECTIONS; i++)
9782     {
9783       for (j = 0; j < 3; j++)
9784       {
9785         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9786   
9787         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9788         {
9789           Tile[xx][yy] = EL_EMPTY;
9790           TEST_DrawLevelField(xx, yy);
9791         }
9792         else
9793           break;
9794       }
9795     }
9796   }
9797 }
9798
9799 static void InitBuggyBase(int x, int y)
9800 {
9801   int element = Tile[x][y];
9802   int activating_delay = FRAMES_PER_SECOND / 4;
9803
9804   ChangeDelay[x][y] =
9805     (element == EL_SP_BUGGY_BASE ?
9806      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9807      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9808      activating_delay :
9809      element == EL_SP_BUGGY_BASE_ACTIVE ?
9810      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9811 }
9812
9813 static void WarnBuggyBase(int x, int y)
9814 {
9815   int i;
9816   static int xy[4][2] =
9817   {
9818     { 0, -1 },
9819     { -1, 0 },
9820     { +1, 0 },
9821     { 0, +1 }
9822   };
9823
9824   for (i = 0; i < NUM_DIRECTIONS; i++)
9825   {
9826     int xx = x + xy[i][0];
9827     int yy = y + xy[i][1];
9828
9829     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9830     {
9831       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9832
9833       break;
9834     }
9835   }
9836 }
9837
9838 static void InitTrap(int x, int y)
9839 {
9840   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9841 }
9842
9843 static void ActivateTrap(int x, int y)
9844 {
9845   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9846 }
9847
9848 static void ChangeActiveTrap(int x, int y)
9849 {
9850   int graphic = IMG_TRAP_ACTIVE;
9851
9852   // if new animation frame was drawn, correct crumbled sand border
9853   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9854     TEST_DrawLevelFieldCrumbled(x, y);
9855 }
9856
9857 static int getSpecialActionElement(int element, int number, int base_element)
9858 {
9859   return (element != EL_EMPTY ? element :
9860           number != -1 ? base_element + number - 1 :
9861           EL_EMPTY);
9862 }
9863
9864 static int getModifiedActionNumber(int value_old, int operator, int operand,
9865                                    int value_min, int value_max)
9866 {
9867   int value_new = (operator == CA_MODE_SET      ? operand :
9868                    operator == CA_MODE_ADD      ? value_old + operand :
9869                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9870                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9871                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9872                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9873                    value_old);
9874
9875   return (value_new < value_min ? value_min :
9876           value_new > value_max ? value_max :
9877           value_new);
9878 }
9879
9880 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9881 {
9882   struct ElementInfo *ei = &element_info[element];
9883   struct ElementChangeInfo *change = &ei->change_page[page];
9884   int target_element = change->target_element;
9885   int action_type = change->action_type;
9886   int action_mode = change->action_mode;
9887   int action_arg = change->action_arg;
9888   int action_element = change->action_element;
9889   int i;
9890
9891   if (!change->has_action)
9892     return;
9893
9894   // ---------- determine action paramater values -----------------------------
9895
9896   int level_time_value =
9897     (level.time > 0 ? TimeLeft :
9898      TimePlayed);
9899
9900   int action_arg_element_raw =
9901     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9902      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9903      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9904      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9905      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9906      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9907      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9908      EL_EMPTY);
9909   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9910
9911   int action_arg_direction =
9912     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9913      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9914      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9915      change->actual_trigger_side :
9916      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9917      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9918      MV_NONE);
9919
9920   int action_arg_number_min =
9921     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9922      CA_ARG_MIN);
9923
9924   int action_arg_number_max =
9925     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9926      action_type == CA_SET_LEVEL_GEMS ? 999 :
9927      action_type == CA_SET_LEVEL_TIME ? 9999 :
9928      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9929      action_type == CA_SET_CE_VALUE ? 9999 :
9930      action_type == CA_SET_CE_SCORE ? 9999 :
9931      CA_ARG_MAX);
9932
9933   int action_arg_number_reset =
9934     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9935      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9936      action_type == CA_SET_LEVEL_TIME ? level.time :
9937      action_type == CA_SET_LEVEL_SCORE ? 0 :
9938      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9939      action_type == CA_SET_CE_SCORE ? 0 :
9940      0);
9941
9942   int action_arg_number =
9943     (action_arg <= CA_ARG_MAX ? action_arg :
9944      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9945      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9946      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9947      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9948      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9949      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9950      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9951      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9952      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9953      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9954      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
9955      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
9956      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9957      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9958      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9959      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9960      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9961      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9962      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9963      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9964      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9965      -1);
9966
9967   int action_arg_number_old =
9968     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
9969      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9970      action_type == CA_SET_LEVEL_SCORE ? game.score :
9971      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9972      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9973      0);
9974
9975   int action_arg_number_new =
9976     getModifiedActionNumber(action_arg_number_old,
9977                             action_mode, action_arg_number,
9978                             action_arg_number_min, action_arg_number_max);
9979
9980   int trigger_player_bits =
9981     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9982      change->actual_trigger_player_bits : change->trigger_player);
9983
9984   int action_arg_player_bits =
9985     (action_arg >= CA_ARG_PLAYER_1 &&
9986      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9987      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9988      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9989      PLAYER_BITS_ANY);
9990
9991   // ---------- execute action  -----------------------------------------------
9992
9993   switch (action_type)
9994   {
9995     case CA_NO_ACTION:
9996     {
9997       return;
9998     }
9999
10000     // ---------- level actions  ----------------------------------------------
10001
10002     case CA_RESTART_LEVEL:
10003     {
10004       game.restart_level = TRUE;
10005
10006       break;
10007     }
10008
10009     case CA_SHOW_ENVELOPE:
10010     {
10011       int element = getSpecialActionElement(action_arg_element,
10012                                             action_arg_number, EL_ENVELOPE_1);
10013
10014       if (IS_ENVELOPE(element))
10015         local_player->show_envelope = element;
10016
10017       break;
10018     }
10019
10020     case CA_SET_LEVEL_TIME:
10021     {
10022       if (level.time > 0)       // only modify limited time value
10023       {
10024         TimeLeft = action_arg_number_new;
10025
10026         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10027
10028         DisplayGameControlValues();
10029
10030         if (!TimeLeft && setup.time_limit)
10031           for (i = 0; i < MAX_PLAYERS; i++)
10032             KillPlayer(&stored_player[i]);
10033       }
10034
10035       break;
10036     }
10037
10038     case CA_SET_LEVEL_SCORE:
10039     {
10040       game.score = action_arg_number_new;
10041
10042       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10043
10044       DisplayGameControlValues();
10045
10046       break;
10047     }
10048
10049     case CA_SET_LEVEL_GEMS:
10050     {
10051       game.gems_still_needed = action_arg_number_new;
10052
10053       game.snapshot.collected_item = TRUE;
10054
10055       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10056
10057       DisplayGameControlValues();
10058
10059       break;
10060     }
10061
10062     case CA_SET_LEVEL_WIND:
10063     {
10064       game.wind_direction = action_arg_direction;
10065
10066       break;
10067     }
10068
10069     case CA_SET_LEVEL_RANDOM_SEED:
10070     {
10071       // ensure that setting a new random seed while playing is predictable
10072       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10073
10074       break;
10075     }
10076
10077     // ---------- player actions  ---------------------------------------------
10078
10079     case CA_MOVE_PLAYER:
10080     case CA_MOVE_PLAYER_NEW:
10081     {
10082       // automatically move to the next field in specified direction
10083       for (i = 0; i < MAX_PLAYERS; i++)
10084         if (trigger_player_bits & (1 << i))
10085           if (action_type == CA_MOVE_PLAYER ||
10086               stored_player[i].MovPos == 0)
10087             stored_player[i].programmed_action = action_arg_direction;
10088
10089       break;
10090     }
10091
10092     case CA_EXIT_PLAYER:
10093     {
10094       for (i = 0; i < MAX_PLAYERS; i++)
10095         if (action_arg_player_bits & (1 << i))
10096           ExitPlayer(&stored_player[i]);
10097
10098       if (game.players_still_needed == 0)
10099         LevelSolved();
10100
10101       break;
10102     }
10103
10104     case CA_KILL_PLAYER:
10105     {
10106       for (i = 0; i < MAX_PLAYERS; i++)
10107         if (action_arg_player_bits & (1 << i))
10108           KillPlayer(&stored_player[i]);
10109
10110       break;
10111     }
10112
10113     case CA_SET_PLAYER_KEYS:
10114     {
10115       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10116       int element = getSpecialActionElement(action_arg_element,
10117                                             action_arg_number, EL_KEY_1);
10118
10119       if (IS_KEY(element))
10120       {
10121         for (i = 0; i < MAX_PLAYERS; i++)
10122         {
10123           if (trigger_player_bits & (1 << i))
10124           {
10125             stored_player[i].key[KEY_NR(element)] = key_state;
10126
10127             DrawGameDoorValues();
10128           }
10129         }
10130       }
10131
10132       break;
10133     }
10134
10135     case CA_SET_PLAYER_SPEED:
10136     {
10137       for (i = 0; i < MAX_PLAYERS; i++)
10138       {
10139         if (trigger_player_bits & (1 << i))
10140         {
10141           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10142
10143           if (action_arg == CA_ARG_SPEED_FASTER &&
10144               stored_player[i].cannot_move)
10145           {
10146             action_arg_number = STEPSIZE_VERY_SLOW;
10147           }
10148           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10149                    action_arg == CA_ARG_SPEED_FASTER)
10150           {
10151             action_arg_number = 2;
10152             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10153                            CA_MODE_MULTIPLY);
10154           }
10155           else if (action_arg == CA_ARG_NUMBER_RESET)
10156           {
10157             action_arg_number = level.initial_player_stepsize[i];
10158           }
10159
10160           move_stepsize =
10161             getModifiedActionNumber(move_stepsize,
10162                                     action_mode,
10163                                     action_arg_number,
10164                                     action_arg_number_min,
10165                                     action_arg_number_max);
10166
10167           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10168         }
10169       }
10170
10171       break;
10172     }
10173
10174     case CA_SET_PLAYER_SHIELD:
10175     {
10176       for (i = 0; i < MAX_PLAYERS; i++)
10177       {
10178         if (trigger_player_bits & (1 << i))
10179         {
10180           if (action_arg == CA_ARG_SHIELD_OFF)
10181           {
10182             stored_player[i].shield_normal_time_left = 0;
10183             stored_player[i].shield_deadly_time_left = 0;
10184           }
10185           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10186           {
10187             stored_player[i].shield_normal_time_left = 999999;
10188           }
10189           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10190           {
10191             stored_player[i].shield_normal_time_left = 999999;
10192             stored_player[i].shield_deadly_time_left = 999999;
10193           }
10194         }
10195       }
10196
10197       break;
10198     }
10199
10200     case CA_SET_PLAYER_GRAVITY:
10201     {
10202       for (i = 0; i < MAX_PLAYERS; i++)
10203       {
10204         if (trigger_player_bits & (1 << i))
10205         {
10206           stored_player[i].gravity =
10207             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10208              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10209              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10210              stored_player[i].gravity);
10211         }
10212       }
10213
10214       break;
10215     }
10216
10217     case CA_SET_PLAYER_ARTWORK:
10218     {
10219       for (i = 0; i < MAX_PLAYERS; i++)
10220       {
10221         if (trigger_player_bits & (1 << i))
10222         {
10223           int artwork_element = action_arg_element;
10224
10225           if (action_arg == CA_ARG_ELEMENT_RESET)
10226             artwork_element =
10227               (level.use_artwork_element[i] ? level.artwork_element[i] :
10228                stored_player[i].element_nr);
10229
10230           if (stored_player[i].artwork_element != artwork_element)
10231             stored_player[i].Frame = 0;
10232
10233           stored_player[i].artwork_element = artwork_element;
10234
10235           SetPlayerWaiting(&stored_player[i], FALSE);
10236
10237           // set number of special actions for bored and sleeping animation
10238           stored_player[i].num_special_action_bored =
10239             get_num_special_action(artwork_element,
10240                                    ACTION_BORING_1, ACTION_BORING_LAST);
10241           stored_player[i].num_special_action_sleeping =
10242             get_num_special_action(artwork_element,
10243                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10244         }
10245       }
10246
10247       break;
10248     }
10249
10250     case CA_SET_PLAYER_INVENTORY:
10251     {
10252       for (i = 0; i < MAX_PLAYERS; i++)
10253       {
10254         struct PlayerInfo *player = &stored_player[i];
10255         int j, k;
10256
10257         if (trigger_player_bits & (1 << i))
10258         {
10259           int inventory_element = action_arg_element;
10260
10261           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10262               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10263               action_arg == CA_ARG_ELEMENT_ACTION)
10264           {
10265             int element = inventory_element;
10266             int collect_count = element_info[element].collect_count_initial;
10267
10268             if (!IS_CUSTOM_ELEMENT(element))
10269               collect_count = 1;
10270
10271             if (collect_count == 0)
10272               player->inventory_infinite_element = element;
10273             else
10274               for (k = 0; k < collect_count; k++)
10275                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10276                   player->inventory_element[player->inventory_size++] =
10277                     element;
10278           }
10279           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10280                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10281                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10282           {
10283             if (player->inventory_infinite_element != EL_UNDEFINED &&
10284                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10285                                      action_arg_element_raw))
10286               player->inventory_infinite_element = EL_UNDEFINED;
10287
10288             for (k = 0, j = 0; j < player->inventory_size; j++)
10289             {
10290               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10291                                         action_arg_element_raw))
10292                 player->inventory_element[k++] = player->inventory_element[j];
10293             }
10294
10295             player->inventory_size = k;
10296           }
10297           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10298           {
10299             if (player->inventory_size > 0)
10300             {
10301               for (j = 0; j < player->inventory_size - 1; j++)
10302                 player->inventory_element[j] = player->inventory_element[j + 1];
10303
10304               player->inventory_size--;
10305             }
10306           }
10307           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10308           {
10309             if (player->inventory_size > 0)
10310               player->inventory_size--;
10311           }
10312           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10313           {
10314             player->inventory_infinite_element = EL_UNDEFINED;
10315             player->inventory_size = 0;
10316           }
10317           else if (action_arg == CA_ARG_INVENTORY_RESET)
10318           {
10319             player->inventory_infinite_element = EL_UNDEFINED;
10320             player->inventory_size = 0;
10321
10322             if (level.use_initial_inventory[i])
10323             {
10324               for (j = 0; j < level.initial_inventory_size[i]; j++)
10325               {
10326                 int element = level.initial_inventory_content[i][j];
10327                 int collect_count = element_info[element].collect_count_initial;
10328
10329                 if (!IS_CUSTOM_ELEMENT(element))
10330                   collect_count = 1;
10331
10332                 if (collect_count == 0)
10333                   player->inventory_infinite_element = element;
10334                 else
10335                   for (k = 0; k < collect_count; k++)
10336                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10337                       player->inventory_element[player->inventory_size++] =
10338                         element;
10339               }
10340             }
10341           }
10342         }
10343       }
10344
10345       break;
10346     }
10347
10348     // ---------- CE actions  -------------------------------------------------
10349
10350     case CA_SET_CE_VALUE:
10351     {
10352       int last_ce_value = CustomValue[x][y];
10353
10354       CustomValue[x][y] = action_arg_number_new;
10355
10356       if (CustomValue[x][y] != last_ce_value)
10357       {
10358         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10359         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10360
10361         if (CustomValue[x][y] == 0)
10362         {
10363           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10364           ChangeCount[x][y] = 0;        // allow at least one more change
10365
10366           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10367           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10368         }
10369       }
10370
10371       break;
10372     }
10373
10374     case CA_SET_CE_SCORE:
10375     {
10376       int last_ce_score = ei->collect_score;
10377
10378       ei->collect_score = action_arg_number_new;
10379
10380       if (ei->collect_score != last_ce_score)
10381       {
10382         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10383         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10384
10385         if (ei->collect_score == 0)
10386         {
10387           int xx, yy;
10388
10389           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10390           ChangeCount[x][y] = 0;        // allow at least one more change
10391
10392           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10393           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10394
10395           /*
10396             This is a very special case that seems to be a mixture between
10397             CheckElementChange() and CheckTriggeredElementChange(): while
10398             the first one only affects single elements that are triggered
10399             directly, the second one affects multiple elements in the playfield
10400             that are triggered indirectly by another element. This is a third
10401             case: Changing the CE score always affects multiple identical CEs,
10402             so every affected CE must be checked, not only the single CE for
10403             which the CE score was changed in the first place (as every instance
10404             of that CE shares the same CE score, and therefore also can change)!
10405           */
10406           SCAN_PLAYFIELD(xx, yy)
10407           {
10408             if (Tile[xx][yy] == element)
10409               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10410                                  CE_SCORE_GETS_ZERO);
10411           }
10412         }
10413       }
10414
10415       break;
10416     }
10417
10418     case CA_SET_CE_ARTWORK:
10419     {
10420       int artwork_element = action_arg_element;
10421       boolean reset_frame = FALSE;
10422       int xx, yy;
10423
10424       if (action_arg == CA_ARG_ELEMENT_RESET)
10425         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10426                            element);
10427
10428       if (ei->gfx_element != artwork_element)
10429         reset_frame = TRUE;
10430
10431       ei->gfx_element = artwork_element;
10432
10433       SCAN_PLAYFIELD(xx, yy)
10434       {
10435         if (Tile[xx][yy] == element)
10436         {
10437           if (reset_frame)
10438           {
10439             ResetGfxAnimation(xx, yy);
10440             ResetRandomAnimationValue(xx, yy);
10441           }
10442
10443           TEST_DrawLevelField(xx, yy);
10444         }
10445       }
10446
10447       break;
10448     }
10449
10450     // ---------- engine actions  ---------------------------------------------
10451
10452     case CA_SET_ENGINE_SCAN_MODE:
10453     {
10454       InitPlayfieldScanMode(action_arg);
10455
10456       break;
10457     }
10458
10459     default:
10460       break;
10461   }
10462 }
10463
10464 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10465 {
10466   int old_element = Tile[x][y];
10467   int new_element = GetElementFromGroupElement(element);
10468   int previous_move_direction = MovDir[x][y];
10469   int last_ce_value = CustomValue[x][y];
10470   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10471   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10472   boolean add_player_onto_element = (new_element_is_player &&
10473                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10474                                      IS_WALKABLE(old_element));
10475
10476   if (!add_player_onto_element)
10477   {
10478     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10479       RemoveMovingField(x, y);
10480     else
10481       RemoveField(x, y);
10482
10483     Tile[x][y] = new_element;
10484
10485     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10486       MovDir[x][y] = previous_move_direction;
10487
10488     if (element_info[new_element].use_last_ce_value)
10489       CustomValue[x][y] = last_ce_value;
10490
10491     InitField_WithBug1(x, y, FALSE);
10492
10493     new_element = Tile[x][y];   // element may have changed
10494
10495     ResetGfxAnimation(x, y);
10496     ResetRandomAnimationValue(x, y);
10497
10498     TEST_DrawLevelField(x, y);
10499
10500     if (GFX_CRUMBLED(new_element))
10501       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10502   }
10503
10504   // check if element under the player changes from accessible to unaccessible
10505   // (needed for special case of dropping element which then changes)
10506   // (must be checked after creating new element for walkable group elements)
10507   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10508       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10509   {
10510     Bang(x, y);
10511
10512     return;
10513   }
10514
10515   // "ChangeCount" not set yet to allow "entered by player" change one time
10516   if (new_element_is_player)
10517     RelocatePlayer(x, y, new_element);
10518
10519   if (is_change)
10520     ChangeCount[x][y]++;        // count number of changes in the same frame
10521
10522   TestIfBadThingTouchesPlayer(x, y);
10523   TestIfPlayerTouchesCustomElement(x, y);
10524   TestIfElementTouchesCustomElement(x, y);
10525 }
10526
10527 static void CreateField(int x, int y, int element)
10528 {
10529   CreateFieldExt(x, y, element, FALSE);
10530 }
10531
10532 static void CreateElementFromChange(int x, int y, int element)
10533 {
10534   element = GET_VALID_RUNTIME_ELEMENT(element);
10535
10536   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10537   {
10538     int old_element = Tile[x][y];
10539
10540     // prevent changed element from moving in same engine frame
10541     // unless both old and new element can either fall or move
10542     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10543         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10544       Stop[x][y] = TRUE;
10545   }
10546
10547   CreateFieldExt(x, y, element, TRUE);
10548 }
10549
10550 static boolean ChangeElement(int x, int y, int element, int page)
10551 {
10552   struct ElementInfo *ei = &element_info[element];
10553   struct ElementChangeInfo *change = &ei->change_page[page];
10554   int ce_value = CustomValue[x][y];
10555   int ce_score = ei->collect_score;
10556   int target_element;
10557   int old_element = Tile[x][y];
10558
10559   // always use default change event to prevent running into a loop
10560   if (ChangeEvent[x][y] == -1)
10561     ChangeEvent[x][y] = CE_DELAY;
10562
10563   if (ChangeEvent[x][y] == CE_DELAY)
10564   {
10565     // reset actual trigger element, trigger player and action element
10566     change->actual_trigger_element = EL_EMPTY;
10567     change->actual_trigger_player = EL_EMPTY;
10568     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10569     change->actual_trigger_side = CH_SIDE_NONE;
10570     change->actual_trigger_ce_value = 0;
10571     change->actual_trigger_ce_score = 0;
10572   }
10573
10574   // do not change elements more than a specified maximum number of changes
10575   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10576     return FALSE;
10577
10578   ChangeCount[x][y]++;          // count number of changes in the same frame
10579
10580   if (change->explode)
10581   {
10582     Bang(x, y);
10583
10584     return TRUE;
10585   }
10586
10587   if (change->use_target_content)
10588   {
10589     boolean complete_replace = TRUE;
10590     boolean can_replace[3][3];
10591     int xx, yy;
10592
10593     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10594     {
10595       boolean is_empty;
10596       boolean is_walkable;
10597       boolean is_diggable;
10598       boolean is_collectible;
10599       boolean is_removable;
10600       boolean is_destructible;
10601       int ex = x + xx - 1;
10602       int ey = y + yy - 1;
10603       int content_element = change->target_content.e[xx][yy];
10604       int e;
10605
10606       can_replace[xx][yy] = TRUE;
10607
10608       if (ex == x && ey == y)   // do not check changing element itself
10609         continue;
10610
10611       if (content_element == EL_EMPTY_SPACE)
10612       {
10613         can_replace[xx][yy] = FALSE;    // do not replace border with space
10614
10615         continue;
10616       }
10617
10618       if (!IN_LEV_FIELD(ex, ey))
10619       {
10620         can_replace[xx][yy] = FALSE;
10621         complete_replace = FALSE;
10622
10623         continue;
10624       }
10625
10626       e = Tile[ex][ey];
10627
10628       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10629         e = MovingOrBlocked2Element(ex, ey);
10630
10631       is_empty = (IS_FREE(ex, ey) ||
10632                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10633
10634       is_walkable     = (is_empty || IS_WALKABLE(e));
10635       is_diggable     = (is_empty || IS_DIGGABLE(e));
10636       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10637       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10638       is_removable    = (is_diggable || is_collectible);
10639
10640       can_replace[xx][yy] =
10641         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10642           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10643           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10644           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10645           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10646           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10647          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10648
10649       if (!can_replace[xx][yy])
10650         complete_replace = FALSE;
10651     }
10652
10653     if (!change->only_if_complete || complete_replace)
10654     {
10655       boolean something_has_changed = FALSE;
10656
10657       if (change->only_if_complete && change->use_random_replace &&
10658           RND(100) < change->random_percentage)
10659         return FALSE;
10660
10661       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10662       {
10663         int ex = x + xx - 1;
10664         int ey = y + yy - 1;
10665         int content_element;
10666
10667         if (can_replace[xx][yy] && (!change->use_random_replace ||
10668                                     RND(100) < change->random_percentage))
10669         {
10670           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10671             RemoveMovingField(ex, ey);
10672
10673           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10674
10675           content_element = change->target_content.e[xx][yy];
10676           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10677                                               ce_value, ce_score);
10678
10679           CreateElementFromChange(ex, ey, target_element);
10680
10681           something_has_changed = TRUE;
10682
10683           // for symmetry reasons, freeze newly created border elements
10684           if (ex != x || ey != y)
10685             Stop[ex][ey] = TRUE;        // no more moving in this frame
10686         }
10687       }
10688
10689       if (something_has_changed)
10690       {
10691         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10692         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10693       }
10694     }
10695   }
10696   else
10697   {
10698     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10699                                         ce_value, ce_score);
10700
10701     if (element == EL_DIAGONAL_GROWING ||
10702         element == EL_DIAGONAL_SHRINKING)
10703     {
10704       target_element = Store[x][y];
10705
10706       Store[x][y] = EL_EMPTY;
10707     }
10708
10709     CreateElementFromChange(x, y, target_element);
10710
10711     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10712     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10713   }
10714
10715   // this uses direct change before indirect change
10716   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10717
10718   return TRUE;
10719 }
10720
10721 static void HandleElementChange(int x, int y, int page)
10722 {
10723   int element = MovingOrBlocked2Element(x, y);
10724   struct ElementInfo *ei = &element_info[element];
10725   struct ElementChangeInfo *change = &ei->change_page[page];
10726   boolean handle_action_before_change = FALSE;
10727
10728 #ifdef DEBUG
10729   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10730       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10731   {
10732     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10733           x, y, element, element_info[element].token_name);
10734     Debug("game:playing:HandleElementChange", "This should never happen!");
10735   }
10736 #endif
10737
10738   // this can happen with classic bombs on walkable, changing elements
10739   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10740   {
10741     return;
10742   }
10743
10744   if (ChangeDelay[x][y] == 0)           // initialize element change
10745   {
10746     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10747
10748     if (change->can_change)
10749     {
10750       // !!! not clear why graphic animation should be reset at all here !!!
10751       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10752       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10753
10754       /*
10755         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10756
10757         When using an animation frame delay of 1 (this only happens with
10758         "sp_zonk.moving.left/right" in the classic graphics), the default
10759         (non-moving) animation shows wrong animation frames (while the
10760         moving animation, like "sp_zonk.moving.left/right", is correct,
10761         so this graphical bug never shows up with the classic graphics).
10762         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10763         be drawn instead of the correct frames 0,1,2,3. This is caused by
10764         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10765         an element change: First when the change delay ("ChangeDelay[][]")
10766         counter has reached zero after decrementing, then a second time in
10767         the next frame (after "GfxFrame[][]" was already incremented) when
10768         "ChangeDelay[][]" is reset to the initial delay value again.
10769
10770         This causes frame 0 to be drawn twice, while the last frame won't
10771         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10772
10773         As some animations may already be cleverly designed around this bug
10774         (at least the "Snake Bite" snake tail animation does this), it cannot
10775         simply be fixed here without breaking such existing animations.
10776         Unfortunately, it cannot easily be detected if a graphics set was
10777         designed "before" or "after" the bug was fixed. As a workaround,
10778         a new graphics set option "game.graphics_engine_version" was added
10779         to be able to specify the game's major release version for which the
10780         graphics set was designed, which can then be used to decide if the
10781         bugfix should be used (version 4 and above) or not (version 3 or
10782         below, or if no version was specified at all, as with old sets).
10783
10784         (The wrong/fixed animation frames can be tested with the test level set
10785         "test_gfxframe" and level "000", which contains a specially prepared
10786         custom element at level position (x/y) == (11/9) which uses the zonk
10787         animation mentioned above. Using "game.graphics_engine_version: 4"
10788         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10789         This can also be seen from the debug output for this test element.)
10790       */
10791
10792       // when a custom element is about to change (for example by change delay),
10793       // do not reset graphic animation when the custom element is moving
10794       if (game.graphics_engine_version < 4 &&
10795           !IS_MOVING(x, y))
10796       {
10797         ResetGfxAnimation(x, y);
10798         ResetRandomAnimationValue(x, y);
10799       }
10800
10801       if (change->pre_change_function)
10802         change->pre_change_function(x, y);
10803     }
10804   }
10805
10806   ChangeDelay[x][y]--;
10807
10808   if (ChangeDelay[x][y] != 0)           // continue element change
10809   {
10810     if (change->can_change)
10811     {
10812       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10813
10814       if (IS_ANIMATED(graphic))
10815         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10816
10817       if (change->change_function)
10818         change->change_function(x, y);
10819     }
10820   }
10821   else                                  // finish element change
10822   {
10823     if (ChangePage[x][y] != -1)         // remember page from delayed change
10824     {
10825       page = ChangePage[x][y];
10826       ChangePage[x][y] = -1;
10827
10828       change = &ei->change_page[page];
10829     }
10830
10831     if (IS_MOVING(x, y))                // never change a running system ;-)
10832     {
10833       ChangeDelay[x][y] = 1;            // try change after next move step
10834       ChangePage[x][y] = page;          // remember page to use for change
10835
10836       return;
10837     }
10838
10839     // special case: set new level random seed before changing element
10840     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10841       handle_action_before_change = TRUE;
10842
10843     if (change->has_action && handle_action_before_change)
10844       ExecuteCustomElementAction(x, y, element, page);
10845
10846     if (change->can_change)
10847     {
10848       if (ChangeElement(x, y, element, page))
10849       {
10850         if (change->post_change_function)
10851           change->post_change_function(x, y);
10852       }
10853     }
10854
10855     if (change->has_action && !handle_action_before_change)
10856       ExecuteCustomElementAction(x, y, element, page);
10857   }
10858 }
10859
10860 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10861                                               int trigger_element,
10862                                               int trigger_event,
10863                                               int trigger_player,
10864                                               int trigger_side,
10865                                               int trigger_page)
10866 {
10867   boolean change_done_any = FALSE;
10868   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10869   int i;
10870
10871   if (!(trigger_events[trigger_element][trigger_event]))
10872     return FALSE;
10873
10874   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10875
10876   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10877   {
10878     int element = EL_CUSTOM_START + i;
10879     boolean change_done = FALSE;
10880     int p;
10881
10882     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10883         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10884       continue;
10885
10886     for (p = 0; p < element_info[element].num_change_pages; p++)
10887     {
10888       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10889
10890       if (change->can_change_or_has_action &&
10891           change->has_event[trigger_event] &&
10892           change->trigger_side & trigger_side &&
10893           change->trigger_player & trigger_player &&
10894           change->trigger_page & trigger_page_bits &&
10895           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10896       {
10897         change->actual_trigger_element = trigger_element;
10898         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10899         change->actual_trigger_player_bits = trigger_player;
10900         change->actual_trigger_side = trigger_side;
10901         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10902         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10903
10904         if ((change->can_change && !change_done) || change->has_action)
10905         {
10906           int x, y;
10907
10908           SCAN_PLAYFIELD(x, y)
10909           {
10910             if (Tile[x][y] == element)
10911             {
10912               if (change->can_change && !change_done)
10913               {
10914                 // if element already changed in this frame, not only prevent
10915                 // another element change (checked in ChangeElement()), but
10916                 // also prevent additional element actions for this element
10917
10918                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10919                     !level.use_action_after_change_bug)
10920                   continue;
10921
10922                 ChangeDelay[x][y] = 1;
10923                 ChangeEvent[x][y] = trigger_event;
10924
10925                 HandleElementChange(x, y, p);
10926               }
10927               else if (change->has_action)
10928               {
10929                 // if element already changed in this frame, not only prevent
10930                 // another element change (checked in ChangeElement()), but
10931                 // also prevent additional element actions for this element
10932
10933                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10934                     !level.use_action_after_change_bug)
10935                   continue;
10936
10937                 ExecuteCustomElementAction(x, y, element, p);
10938                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10939               }
10940             }
10941           }
10942
10943           if (change->can_change)
10944           {
10945             change_done = TRUE;
10946             change_done_any = TRUE;
10947           }
10948         }
10949       }
10950     }
10951   }
10952
10953   RECURSION_LOOP_DETECTION_END();
10954
10955   return change_done_any;
10956 }
10957
10958 static boolean CheckElementChangeExt(int x, int y,
10959                                      int element,
10960                                      int trigger_element,
10961                                      int trigger_event,
10962                                      int trigger_player,
10963                                      int trigger_side)
10964 {
10965   boolean change_done = FALSE;
10966   int p;
10967
10968   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10969       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10970     return FALSE;
10971
10972   if (Tile[x][y] == EL_BLOCKED)
10973   {
10974     Blocked2Moving(x, y, &x, &y);
10975     element = Tile[x][y];
10976   }
10977
10978   // check if element has already changed or is about to change after moving
10979   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10980        Tile[x][y] != element) ||
10981
10982       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10983        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10984         ChangePage[x][y] != -1)))
10985     return FALSE;
10986
10987   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10988
10989   for (p = 0; p < element_info[element].num_change_pages; p++)
10990   {
10991     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10992
10993     /* check trigger element for all events where the element that is checked
10994        for changing interacts with a directly adjacent element -- this is
10995        different to element changes that affect other elements to change on the
10996        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10997     boolean check_trigger_element =
10998       (trigger_event == CE_TOUCHING_X ||
10999        trigger_event == CE_HITTING_X ||
11000        trigger_event == CE_HIT_BY_X ||
11001        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11002
11003     if (change->can_change_or_has_action &&
11004         change->has_event[trigger_event] &&
11005         change->trigger_side & trigger_side &&
11006         change->trigger_player & trigger_player &&
11007         (!check_trigger_element ||
11008          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11009     {
11010       change->actual_trigger_element = trigger_element;
11011       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11012       change->actual_trigger_player_bits = trigger_player;
11013       change->actual_trigger_side = trigger_side;
11014       change->actual_trigger_ce_value = CustomValue[x][y];
11015       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11016
11017       // special case: trigger element not at (x,y) position for some events
11018       if (check_trigger_element)
11019       {
11020         static struct
11021         {
11022           int dx, dy;
11023         } move_xy[] =
11024           {
11025             {  0,  0 },
11026             { -1,  0 },
11027             { +1,  0 },
11028             {  0,  0 },
11029             {  0, -1 },
11030             {  0,  0 }, { 0, 0 }, { 0, 0 },
11031             {  0, +1 }
11032           };
11033
11034         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11035         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11036
11037         change->actual_trigger_ce_value = CustomValue[xx][yy];
11038         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11039       }
11040
11041       if (change->can_change && !change_done)
11042       {
11043         ChangeDelay[x][y] = 1;
11044         ChangeEvent[x][y] = trigger_event;
11045
11046         HandleElementChange(x, y, p);
11047
11048         change_done = TRUE;
11049       }
11050       else if (change->has_action)
11051       {
11052         ExecuteCustomElementAction(x, y, element, p);
11053         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11054       }
11055     }
11056   }
11057
11058   RECURSION_LOOP_DETECTION_END();
11059
11060   return change_done;
11061 }
11062
11063 static void PlayPlayerSound(struct PlayerInfo *player)
11064 {
11065   int jx = player->jx, jy = player->jy;
11066   int sound_element = player->artwork_element;
11067   int last_action = player->last_action_waiting;
11068   int action = player->action_waiting;
11069
11070   if (player->is_waiting)
11071   {
11072     if (action != last_action)
11073       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11074     else
11075       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11076   }
11077   else
11078   {
11079     if (action != last_action)
11080       StopSound(element_info[sound_element].sound[last_action]);
11081
11082     if (last_action == ACTION_SLEEPING)
11083       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11084   }
11085 }
11086
11087 static void PlayAllPlayersSound(void)
11088 {
11089   int i;
11090
11091   for (i = 0; i < MAX_PLAYERS; i++)
11092     if (stored_player[i].active)
11093       PlayPlayerSound(&stored_player[i]);
11094 }
11095
11096 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11097 {
11098   boolean last_waiting = player->is_waiting;
11099   int move_dir = player->MovDir;
11100
11101   player->dir_waiting = move_dir;
11102   player->last_action_waiting = player->action_waiting;
11103
11104   if (is_waiting)
11105   {
11106     if (!last_waiting)          // not waiting -> waiting
11107     {
11108       player->is_waiting = TRUE;
11109
11110       player->frame_counter_bored =
11111         FrameCounter +
11112         game.player_boring_delay_fixed +
11113         GetSimpleRandom(game.player_boring_delay_random);
11114       player->frame_counter_sleeping =
11115         FrameCounter +
11116         game.player_sleeping_delay_fixed +
11117         GetSimpleRandom(game.player_sleeping_delay_random);
11118
11119       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11120     }
11121
11122     if (game.player_sleeping_delay_fixed +
11123         game.player_sleeping_delay_random > 0 &&
11124         player->anim_delay_counter == 0 &&
11125         player->post_delay_counter == 0 &&
11126         FrameCounter >= player->frame_counter_sleeping)
11127       player->is_sleeping = TRUE;
11128     else if (game.player_boring_delay_fixed +
11129              game.player_boring_delay_random > 0 &&
11130              FrameCounter >= player->frame_counter_bored)
11131       player->is_bored = TRUE;
11132
11133     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11134                               player->is_bored ? ACTION_BORING :
11135                               ACTION_WAITING);
11136
11137     if (player->is_sleeping && player->use_murphy)
11138     {
11139       // special case for sleeping Murphy when leaning against non-free tile
11140
11141       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11142           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11143            !IS_MOVING(player->jx - 1, player->jy)))
11144         move_dir = MV_LEFT;
11145       else 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_RIGHT;
11149       else
11150         player->is_sleeping = FALSE;
11151
11152       player->dir_waiting = move_dir;
11153     }
11154
11155     if (player->is_sleeping)
11156     {
11157       if (player->num_special_action_sleeping > 0)
11158       {
11159         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11160         {
11161           int last_special_action = player->special_action_sleeping;
11162           int num_special_action = player->num_special_action_sleeping;
11163           int special_action =
11164             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11165              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11166              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11167              last_special_action + 1 : ACTION_SLEEPING);
11168           int special_graphic =
11169             el_act_dir2img(player->artwork_element, special_action, move_dir);
11170
11171           player->anim_delay_counter =
11172             graphic_info[special_graphic].anim_delay_fixed +
11173             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11174           player->post_delay_counter =
11175             graphic_info[special_graphic].post_delay_fixed +
11176             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11177
11178           player->special_action_sleeping = special_action;
11179         }
11180
11181         if (player->anim_delay_counter > 0)
11182         {
11183           player->action_waiting = player->special_action_sleeping;
11184           player->anim_delay_counter--;
11185         }
11186         else if (player->post_delay_counter > 0)
11187         {
11188           player->post_delay_counter--;
11189         }
11190       }
11191     }
11192     else if (player->is_bored)
11193     {
11194       if (player->num_special_action_bored > 0)
11195       {
11196         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11197         {
11198           int special_action =
11199             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11200           int special_graphic =
11201             el_act_dir2img(player->artwork_element, special_action, move_dir);
11202
11203           player->anim_delay_counter =
11204             graphic_info[special_graphic].anim_delay_fixed +
11205             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11206           player->post_delay_counter =
11207             graphic_info[special_graphic].post_delay_fixed +
11208             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11209
11210           player->special_action_bored = special_action;
11211         }
11212
11213         if (player->anim_delay_counter > 0)
11214         {
11215           player->action_waiting = player->special_action_bored;
11216           player->anim_delay_counter--;
11217         }
11218         else if (player->post_delay_counter > 0)
11219         {
11220           player->post_delay_counter--;
11221         }
11222       }
11223     }
11224   }
11225   else if (last_waiting)        // waiting -> not waiting
11226   {
11227     player->is_waiting = FALSE;
11228     player->is_bored = FALSE;
11229     player->is_sleeping = FALSE;
11230
11231     player->frame_counter_bored = -1;
11232     player->frame_counter_sleeping = -1;
11233
11234     player->anim_delay_counter = 0;
11235     player->post_delay_counter = 0;
11236
11237     player->dir_waiting = player->MovDir;
11238     player->action_waiting = ACTION_DEFAULT;
11239
11240     player->special_action_bored = ACTION_DEFAULT;
11241     player->special_action_sleeping = ACTION_DEFAULT;
11242   }
11243 }
11244
11245 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11246 {
11247   if ((!player->is_moving  && player->was_moving) ||
11248       (player->MovPos == 0 && player->was_moving) ||
11249       (player->is_snapping && !player->was_snapping) ||
11250       (player->is_dropping && !player->was_dropping))
11251   {
11252     if (!CheckSaveEngineSnapshotToList())
11253       return;
11254
11255     player->was_moving = FALSE;
11256     player->was_snapping = TRUE;
11257     player->was_dropping = TRUE;
11258   }
11259   else
11260   {
11261     if (player->is_moving)
11262       player->was_moving = TRUE;
11263
11264     if (!player->is_snapping)
11265       player->was_snapping = FALSE;
11266
11267     if (!player->is_dropping)
11268       player->was_dropping = FALSE;
11269   }
11270
11271   static struct MouseActionInfo mouse_action_last = { 0 };
11272   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11273   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11274
11275   if (new_released)
11276     CheckSaveEngineSnapshotToList();
11277
11278   mouse_action_last = mouse_action;
11279 }
11280
11281 static void CheckSingleStepMode(struct PlayerInfo *player)
11282 {
11283   if (tape.single_step && tape.recording && !tape.pausing)
11284   {
11285     /* as it is called "single step mode", just return to pause mode when the
11286        player stopped moving after one tile (or never starts moving at all) */
11287     if (!player->is_moving &&
11288         !player->is_pushing &&
11289         !player->is_dropping_pressed &&
11290         !player->effective_mouse_action.button)
11291       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11292   }
11293
11294   CheckSaveEngineSnapshot(player);
11295 }
11296
11297 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11298 {
11299   int left      = player_action & JOY_LEFT;
11300   int right     = player_action & JOY_RIGHT;
11301   int up        = player_action & JOY_UP;
11302   int down      = player_action & JOY_DOWN;
11303   int button1   = player_action & JOY_BUTTON_1;
11304   int button2   = player_action & JOY_BUTTON_2;
11305   int dx        = (left ? -1 : right ? 1 : 0);
11306   int dy        = (up   ? -1 : down  ? 1 : 0);
11307
11308   if (!player->active || tape.pausing)
11309     return 0;
11310
11311   if (player_action)
11312   {
11313     if (button1)
11314       SnapField(player, dx, dy);
11315     else
11316     {
11317       if (button2)
11318         DropElement(player);
11319
11320       MovePlayer(player, dx, dy);
11321     }
11322
11323     CheckSingleStepMode(player);
11324
11325     SetPlayerWaiting(player, FALSE);
11326
11327     return player_action;
11328   }
11329   else
11330   {
11331     // no actions for this player (no input at player's configured device)
11332
11333     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11334     SnapField(player, 0, 0);
11335     CheckGravityMovementWhenNotMoving(player);
11336
11337     if (player->MovPos == 0)
11338       SetPlayerWaiting(player, TRUE);
11339
11340     if (player->MovPos == 0)    // needed for tape.playing
11341       player->is_moving = FALSE;
11342
11343     player->is_dropping = FALSE;
11344     player->is_dropping_pressed = FALSE;
11345     player->drop_pressed_delay = 0;
11346
11347     CheckSingleStepMode(player);
11348
11349     return 0;
11350   }
11351 }
11352
11353 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11354                                          byte *tape_action)
11355 {
11356   if (!tape.use_mouse_actions)
11357     return;
11358
11359   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11360   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11361   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11362 }
11363
11364 static void SetTapeActionFromMouseAction(byte *tape_action,
11365                                          struct MouseActionInfo *mouse_action)
11366 {
11367   if (!tape.use_mouse_actions)
11368     return;
11369
11370   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11371   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11372   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11373 }
11374
11375 static void CheckLevelSolved(void)
11376 {
11377   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11378   {
11379     if (game_em.level_solved &&
11380         !game_em.game_over)                             // game won
11381     {
11382       LevelSolved();
11383
11384       game_em.game_over = TRUE;
11385
11386       game.all_players_gone = TRUE;
11387     }
11388
11389     if (game_em.game_over)                              // game lost
11390       game.all_players_gone = TRUE;
11391   }
11392   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11393   {
11394     if (game_sp.level_solved &&
11395         !game_sp.game_over)                             // game won
11396     {
11397       LevelSolved();
11398
11399       game_sp.game_over = TRUE;
11400
11401       game.all_players_gone = TRUE;
11402     }
11403
11404     if (game_sp.game_over)                              // game lost
11405       game.all_players_gone = TRUE;
11406   }
11407   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11408   {
11409     if (game_mm.level_solved &&
11410         !game_mm.game_over)                             // game won
11411     {
11412       LevelSolved();
11413
11414       game_mm.game_over = TRUE;
11415
11416       game.all_players_gone = TRUE;
11417     }
11418
11419     if (game_mm.game_over)                              // game lost
11420       game.all_players_gone = TRUE;
11421   }
11422 }
11423
11424 static void CheckLevelTime(void)
11425 {
11426   int i;
11427
11428   if (TimeFrames >= FRAMES_PER_SECOND)
11429   {
11430     TimeFrames = 0;
11431     TapeTime++;
11432
11433     for (i = 0; i < MAX_PLAYERS; i++)
11434     {
11435       struct PlayerInfo *player = &stored_player[i];
11436
11437       if (SHIELD_ON(player))
11438       {
11439         player->shield_normal_time_left--;
11440
11441         if (player->shield_deadly_time_left > 0)
11442           player->shield_deadly_time_left--;
11443       }
11444     }
11445
11446     if (!game.LevelSolved && !level.use_step_counter)
11447     {
11448       TimePlayed++;
11449
11450       if (TimeLeft > 0)
11451       {
11452         TimeLeft--;
11453
11454         if (TimeLeft <= 10 && setup.time_limit)
11455           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11456
11457         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11458            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11459
11460         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11461
11462         if (!TimeLeft && setup.time_limit)
11463         {
11464           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11465             game_em.lev->killed_out_of_time = TRUE;
11466           else
11467             for (i = 0; i < MAX_PLAYERS; i++)
11468               KillPlayer(&stored_player[i]);
11469         }
11470       }
11471       else if (game.no_time_limit && !game.all_players_gone)
11472       {
11473         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11474       }
11475
11476       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11477     }
11478
11479     if (tape.recording || tape.playing)
11480       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11481   }
11482
11483   if (tape.recording || tape.playing)
11484     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11485
11486   UpdateAndDisplayGameControlValues();
11487 }
11488
11489 void AdvanceFrameAndPlayerCounters(int player_nr)
11490 {
11491   int i;
11492
11493   // advance frame counters (global frame counter and time frame counter)
11494   FrameCounter++;
11495   TimeFrames++;
11496
11497   // advance player counters (counters for move delay, move animation etc.)
11498   for (i = 0; i < MAX_PLAYERS; i++)
11499   {
11500     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11501     int move_delay_value = stored_player[i].move_delay_value;
11502     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11503
11504     if (!advance_player_counters)       // not all players may be affected
11505       continue;
11506
11507     if (move_frames == 0)       // less than one move per game frame
11508     {
11509       int stepsize = TILEX / move_delay_value;
11510       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11511       int count = (stored_player[i].is_moving ?
11512                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11513
11514       if (count % delay == 0)
11515         move_frames = 1;
11516     }
11517
11518     stored_player[i].Frame += move_frames;
11519
11520     if (stored_player[i].MovPos != 0)
11521       stored_player[i].StepFrame += move_frames;
11522
11523     if (stored_player[i].move_delay > 0)
11524       stored_player[i].move_delay--;
11525
11526     // due to bugs in previous versions, counter must count up, not down
11527     if (stored_player[i].push_delay != -1)
11528       stored_player[i].push_delay++;
11529
11530     if (stored_player[i].drop_delay > 0)
11531       stored_player[i].drop_delay--;
11532
11533     if (stored_player[i].is_dropping_pressed)
11534       stored_player[i].drop_pressed_delay++;
11535   }
11536 }
11537
11538 void StartGameActions(boolean init_network_game, boolean record_tape,
11539                       int random_seed)
11540 {
11541   unsigned int new_random_seed = InitRND(random_seed);
11542
11543   if (record_tape)
11544     TapeStartRecording(new_random_seed);
11545
11546   if (init_network_game)
11547   {
11548     SendToServer_LevelFile();
11549     SendToServer_StartPlaying();
11550
11551     return;
11552   }
11553
11554   InitGame();
11555 }
11556
11557 static void GameActionsExt(void)
11558 {
11559 #if 0
11560   static unsigned int game_frame_delay = 0;
11561 #endif
11562   unsigned int game_frame_delay_value;
11563   byte *recorded_player_action;
11564   byte summarized_player_action = 0;
11565   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11566   int i;
11567
11568   // detect endless loops, caused by custom element programming
11569   if (recursion_loop_detected && recursion_loop_depth == 0)
11570   {
11571     char *message = getStringCat3("Internal Error! Element ",
11572                                   EL_NAME(recursion_loop_element),
11573                                   " caused endless loop! Quit the game?");
11574
11575     Warn("element '%s' caused endless loop in game engine",
11576          EL_NAME(recursion_loop_element));
11577
11578     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11579
11580     recursion_loop_detected = FALSE;    // if game should be continued
11581
11582     free(message);
11583
11584     return;
11585   }
11586
11587   if (game.restart_level)
11588     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11589
11590   CheckLevelSolved();
11591
11592   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11593     GameWon();
11594
11595   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11596     TapeStop();
11597
11598   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11599     return;
11600
11601   game_frame_delay_value =
11602     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11603
11604   if (tape.playing && tape.warp_forward && !tape.pausing)
11605     game_frame_delay_value = 0;
11606
11607   SetVideoFrameDelay(game_frame_delay_value);
11608
11609   // (de)activate virtual buttons depending on current game status
11610   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11611   {
11612     if (game.all_players_gone)  // if no players there to be controlled anymore
11613       SetOverlayActive(FALSE);
11614     else if (!tape.playing)     // if game continues after tape stopped playing
11615       SetOverlayActive(TRUE);
11616   }
11617
11618 #if 0
11619 #if 0
11620   // ---------- main game synchronization point ----------
11621
11622   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11623
11624   Debug("game:playing:skip", "skip == %d", skip);
11625
11626 #else
11627   // ---------- main game synchronization point ----------
11628
11629   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11630 #endif
11631 #endif
11632
11633   if (network_playing && !network_player_action_received)
11634   {
11635     // try to get network player actions in time
11636
11637     // last chance to get network player actions without main loop delay
11638     HandleNetworking();
11639
11640     // game was quit by network peer
11641     if (game_status != GAME_MODE_PLAYING)
11642       return;
11643
11644     // check if network player actions still missing and game still running
11645     if (!network_player_action_received && !checkGameEnded())
11646       return;           // failed to get network player actions in time
11647
11648     // do not yet reset "network_player_action_received" (for tape.pausing)
11649   }
11650
11651   if (tape.pausing)
11652     return;
11653
11654   // at this point we know that we really continue executing the game
11655
11656   network_player_action_received = FALSE;
11657
11658   // when playing tape, read previously recorded player input from tape data
11659   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11660
11661   local_player->effective_mouse_action = local_player->mouse_action;
11662
11663   if (recorded_player_action != NULL)
11664     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11665                                  recorded_player_action);
11666
11667   // TapePlayAction() may return NULL when toggling to "pause before death"
11668   if (tape.pausing)
11669     return;
11670
11671   if (tape.set_centered_player)
11672   {
11673     game.centered_player_nr_next = tape.centered_player_nr_next;
11674     game.set_centered_player = TRUE;
11675   }
11676
11677   for (i = 0; i < MAX_PLAYERS; i++)
11678   {
11679     summarized_player_action |= stored_player[i].action;
11680
11681     if (!network_playing && (game.team_mode || tape.playing))
11682       stored_player[i].effective_action = stored_player[i].action;
11683   }
11684
11685   if (network_playing && !checkGameEnded())
11686     SendToServer_MovePlayer(summarized_player_action);
11687
11688   // summarize all actions at local players mapped input device position
11689   // (this allows using different input devices in single player mode)
11690   if (!network.enabled && !game.team_mode)
11691     stored_player[map_player_action[local_player->index_nr]].effective_action =
11692       summarized_player_action;
11693
11694   // summarize all actions at centered player in local team mode
11695   if (tape.recording &&
11696       setup.team_mode && !network.enabled &&
11697       setup.input_on_focus &&
11698       game.centered_player_nr != -1)
11699   {
11700     for (i = 0; i < MAX_PLAYERS; i++)
11701       stored_player[map_player_action[i]].effective_action =
11702         (i == game.centered_player_nr ? summarized_player_action : 0);
11703   }
11704
11705   if (recorded_player_action != NULL)
11706     for (i = 0; i < MAX_PLAYERS; i++)
11707       stored_player[i].effective_action = recorded_player_action[i];
11708
11709   for (i = 0; i < MAX_PLAYERS; i++)
11710   {
11711     tape_action[i] = stored_player[i].effective_action;
11712
11713     /* (this may happen in the RND game engine if a player was not present on
11714        the playfield on level start, but appeared later from a custom element */
11715     if (setup.team_mode &&
11716         tape.recording &&
11717         tape_action[i] &&
11718         !tape.player_participates[i])
11719       tape.player_participates[i] = TRUE;
11720   }
11721
11722   SetTapeActionFromMouseAction(tape_action,
11723                                &local_player->effective_mouse_action);
11724
11725   // only record actions from input devices, but not programmed actions
11726   if (tape.recording)
11727     TapeRecordAction(tape_action);
11728
11729   // remember if game was played (especially after tape stopped playing)
11730   if (!tape.playing && summarized_player_action)
11731     game.GamePlayed = TRUE;
11732
11733 #if USE_NEW_PLAYER_ASSIGNMENTS
11734   // !!! also map player actions in single player mode !!!
11735   // if (game.team_mode)
11736   if (1)
11737   {
11738     byte mapped_action[MAX_PLAYERS];
11739
11740 #if DEBUG_PLAYER_ACTIONS
11741     for (i = 0; i < MAX_PLAYERS; i++)
11742       DebugContinued("", "%d, ", stored_player[i].effective_action);
11743 #endif
11744
11745     for (i = 0; i < MAX_PLAYERS; i++)
11746       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11747
11748     for (i = 0; i < MAX_PLAYERS; i++)
11749       stored_player[i].effective_action = mapped_action[i];
11750
11751 #if DEBUG_PLAYER_ACTIONS
11752     DebugContinued("", "=> ");
11753     for (i = 0; i < MAX_PLAYERS; i++)
11754       DebugContinued("", "%d, ", stored_player[i].effective_action);
11755     DebugContinued("game:playing:player", "\n");
11756 #endif
11757   }
11758 #if DEBUG_PLAYER_ACTIONS
11759   else
11760   {
11761     for (i = 0; i < MAX_PLAYERS; i++)
11762       DebugContinued("", "%d, ", stored_player[i].effective_action);
11763     DebugContinued("game:playing:player", "\n");
11764   }
11765 #endif
11766 #endif
11767
11768   for (i = 0; i < MAX_PLAYERS; i++)
11769   {
11770     // allow engine snapshot in case of changed movement attempt
11771     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11772         (stored_player[i].effective_action & KEY_MOTION))
11773       game.snapshot.changed_action = TRUE;
11774
11775     // allow engine snapshot in case of snapping/dropping attempt
11776     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11777         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11778       game.snapshot.changed_action = TRUE;
11779
11780     game.snapshot.last_action[i] = stored_player[i].effective_action;
11781   }
11782
11783   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11784   {
11785     GameActions_EM_Main();
11786   }
11787   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11788   {
11789     GameActions_SP_Main();
11790   }
11791   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11792   {
11793     GameActions_MM_Main();
11794   }
11795   else
11796   {
11797     GameActions_RND_Main();
11798   }
11799
11800   BlitScreenToBitmap(backbuffer);
11801
11802   CheckLevelSolved();
11803   CheckLevelTime();
11804
11805   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11806
11807   if (global.show_frames_per_second)
11808   {
11809     static unsigned int fps_counter = 0;
11810     static int fps_frames = 0;
11811     unsigned int fps_delay_ms = Counter() - fps_counter;
11812
11813     fps_frames++;
11814
11815     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11816     {
11817       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11818
11819       fps_frames = 0;
11820       fps_counter = Counter();
11821
11822       // always draw FPS to screen after FPS value was updated
11823       redraw_mask |= REDRAW_FPS;
11824     }
11825
11826     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11827     if (GetDrawDeactivationMask() == REDRAW_NONE)
11828       redraw_mask |= REDRAW_FPS;
11829   }
11830 }
11831
11832 static void GameActions_CheckSaveEngineSnapshot(void)
11833 {
11834   if (!game.snapshot.save_snapshot)
11835     return;
11836
11837   // clear flag for saving snapshot _before_ saving snapshot
11838   game.snapshot.save_snapshot = FALSE;
11839
11840   SaveEngineSnapshotToList();
11841 }
11842
11843 void GameActions(void)
11844 {
11845   GameActionsExt();
11846
11847   GameActions_CheckSaveEngineSnapshot();
11848 }
11849
11850 void GameActions_EM_Main(void)
11851 {
11852   byte effective_action[MAX_PLAYERS];
11853   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11854   int i;
11855
11856   for (i = 0; i < MAX_PLAYERS; i++)
11857     effective_action[i] = stored_player[i].effective_action;
11858
11859   GameActions_EM(effective_action, warp_mode);
11860 }
11861
11862 void GameActions_SP_Main(void)
11863 {
11864   byte effective_action[MAX_PLAYERS];
11865   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11866   int i;
11867
11868   for (i = 0; i < MAX_PLAYERS; i++)
11869     effective_action[i] = stored_player[i].effective_action;
11870
11871   GameActions_SP(effective_action, warp_mode);
11872
11873   for (i = 0; i < MAX_PLAYERS; i++)
11874   {
11875     if (stored_player[i].force_dropping)
11876       stored_player[i].action |= KEY_BUTTON_DROP;
11877
11878     stored_player[i].force_dropping = FALSE;
11879   }
11880 }
11881
11882 void GameActions_MM_Main(void)
11883 {
11884   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11885
11886   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11887 }
11888
11889 void GameActions_RND_Main(void)
11890 {
11891   GameActions_RND();
11892 }
11893
11894 void GameActions_RND(void)
11895 {
11896   static struct MouseActionInfo mouse_action_last = { 0 };
11897   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
11898   int magic_wall_x = 0, magic_wall_y = 0;
11899   int i, x, y, element, graphic, last_gfx_frame;
11900
11901   InitPlayfieldScanModeVars();
11902
11903   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11904   {
11905     SCAN_PLAYFIELD(x, y)
11906     {
11907       ChangeCount[x][y] = 0;
11908       ChangeEvent[x][y] = -1;
11909     }
11910   }
11911
11912   if (game.set_centered_player)
11913   {
11914     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11915
11916     // switching to "all players" only possible if all players fit to screen
11917     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11918     {
11919       game.centered_player_nr_next = game.centered_player_nr;
11920       game.set_centered_player = FALSE;
11921     }
11922
11923     // do not switch focus to non-existing (or non-active) player
11924     if (game.centered_player_nr_next >= 0 &&
11925         !stored_player[game.centered_player_nr_next].active)
11926     {
11927       game.centered_player_nr_next = game.centered_player_nr;
11928       game.set_centered_player = FALSE;
11929     }
11930   }
11931
11932   if (game.set_centered_player &&
11933       ScreenMovPos == 0)        // screen currently aligned at tile position
11934   {
11935     int sx, sy;
11936
11937     if (game.centered_player_nr_next == -1)
11938     {
11939       setScreenCenteredToAllPlayers(&sx, &sy);
11940     }
11941     else
11942     {
11943       sx = stored_player[game.centered_player_nr_next].jx;
11944       sy = stored_player[game.centered_player_nr_next].jy;
11945     }
11946
11947     game.centered_player_nr = game.centered_player_nr_next;
11948     game.set_centered_player = FALSE;
11949
11950     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11951     DrawGameDoorValues();
11952   }
11953
11954   for (i = 0; i < MAX_PLAYERS; i++)
11955   {
11956     int actual_player_action = stored_player[i].effective_action;
11957
11958 #if 1
11959     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11960        - rnd_equinox_tetrachloride 048
11961        - rnd_equinox_tetrachloride_ii 096
11962        - rnd_emanuel_schmieg 002
11963        - doctor_sloan_ww 001, 020
11964     */
11965     if (stored_player[i].MovPos == 0)
11966       CheckGravityMovement(&stored_player[i]);
11967 #endif
11968
11969     // overwrite programmed action with tape action
11970     if (stored_player[i].programmed_action)
11971       actual_player_action = stored_player[i].programmed_action;
11972
11973     PlayerActions(&stored_player[i], actual_player_action);
11974
11975     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11976   }
11977
11978   ScrollScreen(NULL, SCROLL_GO_ON);
11979
11980   /* for backwards compatibility, the following code emulates a fixed bug that
11981      occured when pushing elements (causing elements that just made their last
11982      pushing step to already (if possible) make their first falling step in the
11983      same game frame, which is bad); this code is also needed to use the famous
11984      "spring push bug" which is used in older levels and might be wanted to be
11985      used also in newer levels, but in this case the buggy pushing code is only
11986      affecting the "spring" element and no other elements */
11987
11988   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11989   {
11990     for (i = 0; i < MAX_PLAYERS; i++)
11991     {
11992       struct PlayerInfo *player = &stored_player[i];
11993       int x = player->jx;
11994       int y = player->jy;
11995
11996       if (player->active && player->is_pushing && player->is_moving &&
11997           IS_MOVING(x, y) &&
11998           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11999            Tile[x][y] == EL_SPRING))
12000       {
12001         ContinueMoving(x, y);
12002
12003         // continue moving after pushing (this is actually a bug)
12004         if (!IS_MOVING(x, y))
12005           Stop[x][y] = FALSE;
12006       }
12007     }
12008   }
12009
12010   SCAN_PLAYFIELD(x, y)
12011   {
12012     Last[x][y] = Tile[x][y];
12013
12014     ChangeCount[x][y] = 0;
12015     ChangeEvent[x][y] = -1;
12016
12017     // this must be handled before main playfield loop
12018     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12019     {
12020       MovDelay[x][y]--;
12021       if (MovDelay[x][y] <= 0)
12022         RemoveField(x, y);
12023     }
12024
12025     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12026     {
12027       MovDelay[x][y]--;
12028       if (MovDelay[x][y] <= 0)
12029       {
12030         RemoveField(x, y);
12031         TEST_DrawLevelField(x, y);
12032
12033         TestIfElementTouchesCustomElement(x, y);        // for empty space
12034       }
12035     }
12036
12037 #if DEBUG
12038     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12039     {
12040       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12041             x, y);
12042       Debug("game:playing:GameActions_RND", "This should never happen!");
12043
12044       ChangePage[x][y] = -1;
12045     }
12046 #endif
12047
12048     Stop[x][y] = FALSE;
12049     if (WasJustMoving[x][y] > 0)
12050       WasJustMoving[x][y]--;
12051     if (WasJustFalling[x][y] > 0)
12052       WasJustFalling[x][y]--;
12053     if (CheckCollision[x][y] > 0)
12054       CheckCollision[x][y]--;
12055     if (CheckImpact[x][y] > 0)
12056       CheckImpact[x][y]--;
12057
12058     GfxFrame[x][y]++;
12059
12060     /* reset finished pushing action (not done in ContinueMoving() to allow
12061        continuous pushing animation for elements with zero push delay) */
12062     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12063     {
12064       ResetGfxAnimation(x, y);
12065       TEST_DrawLevelField(x, y);
12066     }
12067
12068 #if DEBUG
12069     if (IS_BLOCKED(x, y))
12070     {
12071       int oldx, oldy;
12072
12073       Blocked2Moving(x, y, &oldx, &oldy);
12074       if (!IS_MOVING(oldx, oldy))
12075       {
12076         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12077         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12078         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12079         Debug("game:playing:GameActions_RND", "This should never happen!");
12080       }
12081     }
12082 #endif
12083   }
12084
12085   if (mouse_action.button)
12086   {
12087     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12088
12089     x = mouse_action.lx;
12090     y = mouse_action.ly;
12091     element = Tile[x][y];
12092
12093     if (new_button)
12094     {
12095       CheckElementChange(x, y, element, EL_UNDEFINED, CE_CLICKED_BY_MOUSE);
12096       CheckTriggeredElementChange(x, y, element, CE_MOUSE_CLICKED_ON_X);
12097     }
12098
12099     CheckElementChange(x, y, element, EL_UNDEFINED, CE_PRESSED_BY_MOUSE);
12100     CheckTriggeredElementChange(x, y, element, CE_MOUSE_PRESSED_ON_X);
12101   }
12102
12103   SCAN_PLAYFIELD(x, y)
12104   {
12105     element = Tile[x][y];
12106     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12107     last_gfx_frame = GfxFrame[x][y];
12108
12109     ResetGfxFrame(x, y);
12110
12111     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12112       DrawLevelGraphicAnimation(x, y, graphic);
12113
12114     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12115         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12116       ResetRandomAnimationValue(x, y);
12117
12118     SetRandomAnimationValue(x, y);
12119
12120     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12121
12122     if (IS_INACTIVE(element))
12123     {
12124       if (IS_ANIMATED(graphic))
12125         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12126
12127       continue;
12128     }
12129
12130     // this may take place after moving, so 'element' may have changed
12131     if (IS_CHANGING(x, y) &&
12132         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12133     {
12134       int page = element_info[element].event_page_nr[CE_DELAY];
12135
12136       HandleElementChange(x, y, page);
12137
12138       element = Tile[x][y];
12139       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12140     }
12141
12142     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12143     {
12144       StartMoving(x, y);
12145
12146       element = Tile[x][y];
12147       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12148
12149       if (IS_ANIMATED(graphic) &&
12150           !IS_MOVING(x, y) &&
12151           !Stop[x][y])
12152         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12153
12154       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12155         TEST_DrawTwinkleOnField(x, y);
12156     }
12157     else if (element == EL_ACID)
12158     {
12159       if (!Stop[x][y])
12160         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12161     }
12162     else if ((element == EL_EXIT_OPEN ||
12163               element == EL_EM_EXIT_OPEN ||
12164               element == EL_SP_EXIT_OPEN ||
12165               element == EL_STEEL_EXIT_OPEN ||
12166               element == EL_EM_STEEL_EXIT_OPEN ||
12167               element == EL_SP_TERMINAL ||
12168               element == EL_SP_TERMINAL_ACTIVE ||
12169               element == EL_EXTRA_TIME ||
12170               element == EL_SHIELD_NORMAL ||
12171               element == EL_SHIELD_DEADLY) &&
12172              IS_ANIMATED(graphic))
12173       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12174     else if (IS_MOVING(x, y))
12175       ContinueMoving(x, y);
12176     else if (IS_ACTIVE_BOMB(element))
12177       CheckDynamite(x, y);
12178     else if (element == EL_AMOEBA_GROWING)
12179       AmoebaGrowing(x, y);
12180     else if (element == EL_AMOEBA_SHRINKING)
12181       AmoebaShrinking(x, y);
12182
12183 #if !USE_NEW_AMOEBA_CODE
12184     else if (IS_AMOEBALIVE(element))
12185       AmoebaReproduce(x, y);
12186 #endif
12187
12188     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12189       Life(x, y);
12190     else if (element == EL_EXIT_CLOSED)
12191       CheckExit(x, y);
12192     else if (element == EL_EM_EXIT_CLOSED)
12193       CheckExitEM(x, y);
12194     else if (element == EL_STEEL_EXIT_CLOSED)
12195       CheckExitSteel(x, y);
12196     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12197       CheckExitSteelEM(x, y);
12198     else if (element == EL_SP_EXIT_CLOSED)
12199       CheckExitSP(x, y);
12200     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12201              element == EL_EXPANDABLE_STEELWALL_GROWING)
12202       MauerWaechst(x, y);
12203     else if (element == EL_EXPANDABLE_WALL ||
12204              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12205              element == EL_EXPANDABLE_WALL_VERTICAL ||
12206              element == EL_EXPANDABLE_WALL_ANY ||
12207              element == EL_BD_EXPANDABLE_WALL)
12208       MauerAbleger(x, y);
12209     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12210              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12211              element == EL_EXPANDABLE_STEELWALL_ANY)
12212       MauerAblegerStahl(x, y);
12213     else if (element == EL_FLAMES)
12214       CheckForDragon(x, y);
12215     else if (element == EL_EXPLOSION)
12216       ; // drawing of correct explosion animation is handled separately
12217     else if (element == EL_ELEMENT_SNAPPING ||
12218              element == EL_DIAGONAL_SHRINKING ||
12219              element == EL_DIAGONAL_GROWING)
12220     {
12221       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12222
12223       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12224     }
12225     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12226       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12227
12228     if (IS_BELT_ACTIVE(element))
12229       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12230
12231     if (game.magic_wall_active)
12232     {
12233       int jx = local_player->jx, jy = local_player->jy;
12234
12235       // play the element sound at the position nearest to the player
12236       if ((element == EL_MAGIC_WALL_FULL ||
12237            element == EL_MAGIC_WALL_ACTIVE ||
12238            element == EL_MAGIC_WALL_EMPTYING ||
12239            element == EL_BD_MAGIC_WALL_FULL ||
12240            element == EL_BD_MAGIC_WALL_ACTIVE ||
12241            element == EL_BD_MAGIC_WALL_EMPTYING ||
12242            element == EL_DC_MAGIC_WALL_FULL ||
12243            element == EL_DC_MAGIC_WALL_ACTIVE ||
12244            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12245           ABS(x - jx) + ABS(y - jy) <
12246           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12247       {
12248         magic_wall_x = x;
12249         magic_wall_y = y;
12250       }
12251     }
12252   }
12253
12254 #if USE_NEW_AMOEBA_CODE
12255   // new experimental amoeba growth stuff
12256   if (!(FrameCounter % 8))
12257   {
12258     static unsigned int random = 1684108901;
12259
12260     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12261     {
12262       x = RND(lev_fieldx);
12263       y = RND(lev_fieldy);
12264       element = Tile[x][y];
12265
12266       if (!IS_PLAYER(x,y) &&
12267           (element == EL_EMPTY ||
12268            CAN_GROW_INTO(element) ||
12269            element == EL_QUICKSAND_EMPTY ||
12270            element == EL_QUICKSAND_FAST_EMPTY ||
12271            element == EL_ACID_SPLASH_LEFT ||
12272            element == EL_ACID_SPLASH_RIGHT))
12273       {
12274         if ((IN_LEV_FIELD(x, y-1) && Tile[x][y-1] == EL_AMOEBA_WET) ||
12275             (IN_LEV_FIELD(x-1, y) && Tile[x-1][y] == EL_AMOEBA_WET) ||
12276             (IN_LEV_FIELD(x+1, y) && Tile[x+1][y] == EL_AMOEBA_WET) ||
12277             (IN_LEV_FIELD(x, y+1) && Tile[x][y+1] == EL_AMOEBA_WET))
12278           Tile[x][y] = EL_AMOEBA_DROP;
12279       }
12280
12281       random = random * 129 + 1;
12282     }
12283   }
12284 #endif
12285
12286   game.explosions_delayed = FALSE;
12287
12288   SCAN_PLAYFIELD(x, y)
12289   {
12290     element = Tile[x][y];
12291
12292     if (ExplodeField[x][y])
12293       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12294     else if (element == EL_EXPLOSION)
12295       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12296
12297     ExplodeField[x][y] = EX_TYPE_NONE;
12298   }
12299
12300   game.explosions_delayed = TRUE;
12301
12302   if (game.magic_wall_active)
12303   {
12304     if (!(game.magic_wall_time_left % 4))
12305     {
12306       int element = Tile[magic_wall_x][magic_wall_y];
12307
12308       if (element == EL_BD_MAGIC_WALL_FULL ||
12309           element == EL_BD_MAGIC_WALL_ACTIVE ||
12310           element == EL_BD_MAGIC_WALL_EMPTYING)
12311         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12312       else if (element == EL_DC_MAGIC_WALL_FULL ||
12313                element == EL_DC_MAGIC_WALL_ACTIVE ||
12314                element == EL_DC_MAGIC_WALL_EMPTYING)
12315         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12316       else
12317         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12318     }
12319
12320     if (game.magic_wall_time_left > 0)
12321     {
12322       game.magic_wall_time_left--;
12323
12324       if (!game.magic_wall_time_left)
12325       {
12326         SCAN_PLAYFIELD(x, y)
12327         {
12328           element = Tile[x][y];
12329
12330           if (element == EL_MAGIC_WALL_ACTIVE ||
12331               element == EL_MAGIC_WALL_FULL)
12332           {
12333             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12334             TEST_DrawLevelField(x, y);
12335           }
12336           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12337                    element == EL_BD_MAGIC_WALL_FULL)
12338           {
12339             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12340             TEST_DrawLevelField(x, y);
12341           }
12342           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12343                    element == EL_DC_MAGIC_WALL_FULL)
12344           {
12345             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12346             TEST_DrawLevelField(x, y);
12347           }
12348         }
12349
12350         game.magic_wall_active = FALSE;
12351       }
12352     }
12353   }
12354
12355   if (game.light_time_left > 0)
12356   {
12357     game.light_time_left--;
12358
12359     if (game.light_time_left == 0)
12360       RedrawAllLightSwitchesAndInvisibleElements();
12361   }
12362
12363   if (game.timegate_time_left > 0)
12364   {
12365     game.timegate_time_left--;
12366
12367     if (game.timegate_time_left == 0)
12368       CloseAllOpenTimegates();
12369   }
12370
12371   if (game.lenses_time_left > 0)
12372   {
12373     game.lenses_time_left--;
12374
12375     if (game.lenses_time_left == 0)
12376       RedrawAllInvisibleElementsForLenses();
12377   }
12378
12379   if (game.magnify_time_left > 0)
12380   {
12381     game.magnify_time_left--;
12382
12383     if (game.magnify_time_left == 0)
12384       RedrawAllInvisibleElementsForMagnifier();
12385   }
12386
12387   for (i = 0; i < MAX_PLAYERS; i++)
12388   {
12389     struct PlayerInfo *player = &stored_player[i];
12390
12391     if (SHIELD_ON(player))
12392     {
12393       if (player->shield_deadly_time_left)
12394         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12395       else if (player->shield_normal_time_left)
12396         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12397     }
12398   }
12399
12400 #if USE_DELAYED_GFX_REDRAW
12401   SCAN_PLAYFIELD(x, y)
12402   {
12403     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12404     {
12405       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12406          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12407
12408       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12409         DrawLevelField(x, y);
12410
12411       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12412         DrawLevelFieldCrumbled(x, y);
12413
12414       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12415         DrawLevelFieldCrumbledNeighbours(x, y);
12416
12417       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12418         DrawTwinkleOnField(x, y);
12419     }
12420
12421     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12422   }
12423 #endif
12424
12425   DrawAllPlayers();
12426   PlayAllPlayersSound();
12427
12428   for (i = 0; i < MAX_PLAYERS; i++)
12429   {
12430     struct PlayerInfo *player = &stored_player[i];
12431
12432     if (player->show_envelope != 0 && (!player->active ||
12433                                        player->MovPos == 0))
12434     {
12435       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12436
12437       player->show_envelope = 0;
12438     }
12439   }
12440
12441   // use random number generator in every frame to make it less predictable
12442   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12443     RND(1);
12444
12445   mouse_action_last = mouse_action;
12446 }
12447
12448 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12449 {
12450   int min_x = x, min_y = y, max_x = x, max_y = y;
12451   int i;
12452
12453   for (i = 0; i < MAX_PLAYERS; i++)
12454   {
12455     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12456
12457     if (!stored_player[i].active || &stored_player[i] == player)
12458       continue;
12459
12460     min_x = MIN(min_x, jx);
12461     min_y = MIN(min_y, jy);
12462     max_x = MAX(max_x, jx);
12463     max_y = MAX(max_y, jy);
12464   }
12465
12466   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12467 }
12468
12469 static boolean AllPlayersInVisibleScreen(void)
12470 {
12471   int i;
12472
12473   for (i = 0; i < MAX_PLAYERS; i++)
12474   {
12475     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12476
12477     if (!stored_player[i].active)
12478       continue;
12479
12480     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12481       return FALSE;
12482   }
12483
12484   return TRUE;
12485 }
12486
12487 void ScrollLevel(int dx, int dy)
12488 {
12489   int scroll_offset = 2 * TILEX_VAR;
12490   int x, y;
12491
12492   BlitBitmap(drawto_field, drawto_field,
12493              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12494              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12495              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12496              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12497              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12498              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12499
12500   if (dx != 0)
12501   {
12502     x = (dx == 1 ? BX1 : BX2);
12503     for (y = BY1; y <= BY2; y++)
12504       DrawScreenField(x, y);
12505   }
12506
12507   if (dy != 0)
12508   {
12509     y = (dy == 1 ? BY1 : BY2);
12510     for (x = BX1; x <= BX2; x++)
12511       DrawScreenField(x, y);
12512   }
12513
12514   redraw_mask |= REDRAW_FIELD;
12515 }
12516
12517 static boolean canFallDown(struct PlayerInfo *player)
12518 {
12519   int jx = player->jx, jy = player->jy;
12520
12521   return (IN_LEV_FIELD(jx, jy + 1) &&
12522           (IS_FREE(jx, jy + 1) ||
12523            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12524           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12525           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12526 }
12527
12528 static boolean canPassField(int x, int y, int move_dir)
12529 {
12530   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12531   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12532   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12533   int nextx = x + dx;
12534   int nexty = y + dy;
12535   int element = Tile[x][y];
12536
12537   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12538           !CAN_MOVE(element) &&
12539           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12540           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12541           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12542 }
12543
12544 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12545 {
12546   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12547   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12548   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12549   int newx = x + dx;
12550   int newy = y + dy;
12551
12552   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12553           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12554           (IS_DIGGABLE(Tile[newx][newy]) ||
12555            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12556            canPassField(newx, newy, move_dir)));
12557 }
12558
12559 static void CheckGravityMovement(struct PlayerInfo *player)
12560 {
12561   if (player->gravity && !player->programmed_action)
12562   {
12563     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12564     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12565     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12566     int jx = player->jx, jy = player->jy;
12567     boolean player_is_moving_to_valid_field =
12568       (!player_is_snapping &&
12569        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12570         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12571     boolean player_can_fall_down = canFallDown(player);
12572
12573     if (player_can_fall_down &&
12574         !player_is_moving_to_valid_field)
12575       player->programmed_action = MV_DOWN;
12576   }
12577 }
12578
12579 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12580 {
12581   return CheckGravityMovement(player);
12582
12583   if (player->gravity && !player->programmed_action)
12584   {
12585     int jx = player->jx, jy = player->jy;
12586     boolean field_under_player_is_free =
12587       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12588     boolean player_is_standing_on_valid_field =
12589       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12590        (IS_WALKABLE(Tile[jx][jy]) &&
12591         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12592
12593     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12594       player->programmed_action = MV_DOWN;
12595   }
12596 }
12597
12598 /*
12599   MovePlayerOneStep()
12600   -----------------------------------------------------------------------------
12601   dx, dy:               direction (non-diagonal) to try to move the player to
12602   real_dx, real_dy:     direction as read from input device (can be diagonal)
12603 */
12604
12605 boolean MovePlayerOneStep(struct PlayerInfo *player,
12606                           int dx, int dy, int real_dx, int real_dy)
12607 {
12608   int jx = player->jx, jy = player->jy;
12609   int new_jx = jx + dx, new_jy = jy + dy;
12610   int can_move;
12611   boolean player_can_move = !player->cannot_move;
12612
12613   if (!player->active || (!dx && !dy))
12614     return MP_NO_ACTION;
12615
12616   player->MovDir = (dx < 0 ? MV_LEFT :
12617                     dx > 0 ? MV_RIGHT :
12618                     dy < 0 ? MV_UP :
12619                     dy > 0 ? MV_DOWN :  MV_NONE);
12620
12621   if (!IN_LEV_FIELD(new_jx, new_jy))
12622     return MP_NO_ACTION;
12623
12624   if (!player_can_move)
12625   {
12626     if (player->MovPos == 0)
12627     {
12628       player->is_moving = FALSE;
12629       player->is_digging = FALSE;
12630       player->is_collecting = FALSE;
12631       player->is_snapping = FALSE;
12632       player->is_pushing = FALSE;
12633     }
12634   }
12635
12636   if (!network.enabled && game.centered_player_nr == -1 &&
12637       !AllPlayersInSight(player, new_jx, new_jy))
12638     return MP_NO_ACTION;
12639
12640   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12641   if (can_move != MP_MOVING)
12642     return can_move;
12643
12644   // check if DigField() has caused relocation of the player
12645   if (player->jx != jx || player->jy != jy)
12646     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12647
12648   StorePlayer[jx][jy] = 0;
12649   player->last_jx = jx;
12650   player->last_jy = jy;
12651   player->jx = new_jx;
12652   player->jy = new_jy;
12653   StorePlayer[new_jx][new_jy] = player->element_nr;
12654
12655   if (player->move_delay_value_next != -1)
12656   {
12657     player->move_delay_value = player->move_delay_value_next;
12658     player->move_delay_value_next = -1;
12659   }
12660
12661   player->MovPos =
12662     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12663
12664   player->step_counter++;
12665
12666   PlayerVisit[jx][jy] = FrameCounter;
12667
12668   player->is_moving = TRUE;
12669
12670 #if 1
12671   // should better be called in MovePlayer(), but this breaks some tapes
12672   ScrollPlayer(player, SCROLL_INIT);
12673 #endif
12674
12675   return MP_MOVING;
12676 }
12677
12678 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12679 {
12680   int jx = player->jx, jy = player->jy;
12681   int old_jx = jx, old_jy = jy;
12682   int moved = MP_NO_ACTION;
12683
12684   if (!player->active)
12685     return FALSE;
12686
12687   if (!dx && !dy)
12688   {
12689     if (player->MovPos == 0)
12690     {
12691       player->is_moving = FALSE;
12692       player->is_digging = FALSE;
12693       player->is_collecting = FALSE;
12694       player->is_snapping = FALSE;
12695       player->is_pushing = FALSE;
12696     }
12697
12698     return FALSE;
12699   }
12700
12701   if (player->move_delay > 0)
12702     return FALSE;
12703
12704   player->move_delay = -1;              // set to "uninitialized" value
12705
12706   // store if player is automatically moved to next field
12707   player->is_auto_moving = (player->programmed_action != MV_NONE);
12708
12709   // remove the last programmed player action
12710   player->programmed_action = 0;
12711
12712   if (player->MovPos)
12713   {
12714     // should only happen if pre-1.2 tape recordings are played
12715     // this is only for backward compatibility
12716
12717     int original_move_delay_value = player->move_delay_value;
12718
12719 #if DEBUG
12720     Debug("game:playing:MovePlayer",
12721           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12722           tape.counter);
12723 #endif
12724
12725     // scroll remaining steps with finest movement resolution
12726     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12727
12728     while (player->MovPos)
12729     {
12730       ScrollPlayer(player, SCROLL_GO_ON);
12731       ScrollScreen(NULL, SCROLL_GO_ON);
12732
12733       AdvanceFrameAndPlayerCounters(player->index_nr);
12734
12735       DrawAllPlayers();
12736       BackToFront_WithFrameDelay(0);
12737     }
12738
12739     player->move_delay_value = original_move_delay_value;
12740   }
12741
12742   player->is_active = FALSE;
12743
12744   if (player->last_move_dir & MV_HORIZONTAL)
12745   {
12746     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12747       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12748   }
12749   else
12750   {
12751     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12752       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12753   }
12754
12755   if (!moved && !player->is_active)
12756   {
12757     player->is_moving = FALSE;
12758     player->is_digging = FALSE;
12759     player->is_collecting = FALSE;
12760     player->is_snapping = FALSE;
12761     player->is_pushing = FALSE;
12762   }
12763
12764   jx = player->jx;
12765   jy = player->jy;
12766
12767   if (moved & MP_MOVING && !ScreenMovPos &&
12768       (player->index_nr == game.centered_player_nr ||
12769        game.centered_player_nr == -1))
12770   {
12771     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12772
12773     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12774     {
12775       // actual player has left the screen -- scroll in that direction
12776       if (jx != old_jx)         // player has moved horizontally
12777         scroll_x += (jx - old_jx);
12778       else                      // player has moved vertically
12779         scroll_y += (jy - old_jy);
12780     }
12781     else
12782     {
12783       int offset_raw = game.scroll_delay_value;
12784
12785       if (jx != old_jx)         // player has moved horizontally
12786       {
12787         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12788         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12789         int new_scroll_x = jx - MIDPOSX + offset_x;
12790
12791         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12792             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12793           scroll_x = new_scroll_x;
12794
12795         // don't scroll over playfield boundaries
12796         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12797
12798         // don't scroll more than one field at a time
12799         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12800
12801         // don't scroll against the player's moving direction
12802         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12803             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12804           scroll_x = old_scroll_x;
12805       }
12806       else                      // player has moved vertically
12807       {
12808         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12809         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12810         int new_scroll_y = jy - MIDPOSY + offset_y;
12811
12812         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12813             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12814           scroll_y = new_scroll_y;
12815
12816         // don't scroll over playfield boundaries
12817         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
12818
12819         // don't scroll more than one field at a time
12820         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12821
12822         // don't scroll against the player's moving direction
12823         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12824             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12825           scroll_y = old_scroll_y;
12826       }
12827     }
12828
12829     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12830     {
12831       if (!network.enabled && game.centered_player_nr == -1 &&
12832           !AllPlayersInVisibleScreen())
12833       {
12834         scroll_x = old_scroll_x;
12835         scroll_y = old_scroll_y;
12836       }
12837       else
12838       {
12839         ScrollScreen(player, SCROLL_INIT);
12840         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12841       }
12842     }
12843   }
12844
12845   player->StepFrame = 0;
12846
12847   if (moved & MP_MOVING)
12848   {
12849     if (old_jx != jx && old_jy == jy)
12850       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12851     else if (old_jx == jx && old_jy != jy)
12852       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12853
12854     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12855
12856     player->last_move_dir = player->MovDir;
12857     player->is_moving = TRUE;
12858     player->is_snapping = FALSE;
12859     player->is_switching = FALSE;
12860     player->is_dropping = FALSE;
12861     player->is_dropping_pressed = FALSE;
12862     player->drop_pressed_delay = 0;
12863
12864 #if 0
12865     // should better be called here than above, but this breaks some tapes
12866     ScrollPlayer(player, SCROLL_INIT);
12867 #endif
12868   }
12869   else
12870   {
12871     CheckGravityMovementWhenNotMoving(player);
12872
12873     player->is_moving = FALSE;
12874
12875     /* at this point, the player is allowed to move, but cannot move right now
12876        (e.g. because of something blocking the way) -- ensure that the player
12877        is also allowed to move in the next frame (in old versions before 3.1.1,
12878        the player was forced to wait again for eight frames before next try) */
12879
12880     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12881       player->move_delay = 0;   // allow direct movement in the next frame
12882   }
12883
12884   if (player->move_delay == -1)         // not yet initialized by DigField()
12885     player->move_delay = player->move_delay_value;
12886
12887   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12888   {
12889     TestIfPlayerTouchesBadThing(jx, jy);
12890     TestIfPlayerTouchesCustomElement(jx, jy);
12891   }
12892
12893   if (!player->active)
12894     RemovePlayer(player);
12895
12896   return moved;
12897 }
12898
12899 void ScrollPlayer(struct PlayerInfo *player, int mode)
12900 {
12901   int jx = player->jx, jy = player->jy;
12902   int last_jx = player->last_jx, last_jy = player->last_jy;
12903   int move_stepsize = TILEX / player->move_delay_value;
12904
12905   if (!player->active)
12906     return;
12907
12908   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
12909     return;
12910
12911   if (mode == SCROLL_INIT)
12912   {
12913     player->actual_frame_counter = FrameCounter;
12914     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12915
12916     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12917         Tile[last_jx][last_jy] == EL_EMPTY)
12918     {
12919       int last_field_block_delay = 0;   // start with no blocking at all
12920       int block_delay_adjustment = player->block_delay_adjustment;
12921
12922       // if player blocks last field, add delay for exactly one move
12923       if (player->block_last_field)
12924       {
12925         last_field_block_delay += player->move_delay_value;
12926
12927         // when blocking enabled, prevent moving up despite gravity
12928         if (player->gravity && player->MovDir == MV_UP)
12929           block_delay_adjustment = -1;
12930       }
12931
12932       // add block delay adjustment (also possible when not blocking)
12933       last_field_block_delay += block_delay_adjustment;
12934
12935       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12936       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12937     }
12938
12939     if (player->MovPos != 0)    // player has not yet reached destination
12940       return;
12941   }
12942   else if (!FrameReached(&player->actual_frame_counter, 1))
12943     return;
12944
12945   if (player->MovPos != 0)
12946   {
12947     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12948     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12949
12950     // before DrawPlayer() to draw correct player graphic for this case
12951     if (player->MovPos == 0)
12952       CheckGravityMovement(player);
12953   }
12954
12955   if (player->MovPos == 0)      // player reached destination field
12956   {
12957     if (player->move_delay_reset_counter > 0)
12958     {
12959       player->move_delay_reset_counter--;
12960
12961       if (player->move_delay_reset_counter == 0)
12962       {
12963         // continue with normal speed after quickly moving through gate
12964         HALVE_PLAYER_SPEED(player);
12965
12966         // be able to make the next move without delay
12967         player->move_delay = 0;
12968       }
12969     }
12970
12971     player->last_jx = jx;
12972     player->last_jy = jy;
12973
12974     if (Tile[jx][jy] == EL_EXIT_OPEN ||
12975         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
12976         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
12977         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
12978         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12979         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12980         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
12981         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
12982     {
12983       ExitPlayer(player);
12984
12985       if (game.players_still_needed == 0 &&
12986           (game.friends_still_needed == 0 ||
12987            IS_SP_ELEMENT(Tile[jx][jy])))
12988         LevelSolved();
12989     }
12990
12991     // this breaks one level: "machine", level 000
12992     {
12993       int move_direction = player->MovDir;
12994       int enter_side = MV_DIR_OPPOSITE(move_direction);
12995       int leave_side = move_direction;
12996       int old_jx = last_jx;
12997       int old_jy = last_jy;
12998       int old_element = Tile[old_jx][old_jy];
12999       int new_element = Tile[jx][jy];
13000
13001       if (IS_CUSTOM_ELEMENT(old_element))
13002         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13003                                    CE_LEFT_BY_PLAYER,
13004                                    player->index_bit, leave_side);
13005
13006       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13007                                           CE_PLAYER_LEAVES_X,
13008                                           player->index_bit, leave_side);
13009
13010       if (IS_CUSTOM_ELEMENT(new_element))
13011         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13012                                    player->index_bit, enter_side);
13013
13014       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13015                                           CE_PLAYER_ENTERS_X,
13016                                           player->index_bit, enter_side);
13017
13018       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13019                                         CE_MOVE_OF_X, move_direction);
13020     }
13021
13022     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13023     {
13024       TestIfPlayerTouchesBadThing(jx, jy);
13025       TestIfPlayerTouchesCustomElement(jx, jy);
13026
13027       /* needed because pushed element has not yet reached its destination,
13028          so it would trigger a change event at its previous field location */
13029       if (!player->is_pushing)
13030         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13031
13032       if (!player->active)
13033         RemovePlayer(player);
13034     }
13035
13036     if (!game.LevelSolved && level.use_step_counter)
13037     {
13038       int i;
13039
13040       TimePlayed++;
13041
13042       if (TimeLeft > 0)
13043       {
13044         TimeLeft--;
13045
13046         if (TimeLeft <= 10 && setup.time_limit)
13047           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13048
13049         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13050
13051         DisplayGameControlValues();
13052
13053         if (!TimeLeft && setup.time_limit)
13054           for (i = 0; i < MAX_PLAYERS; i++)
13055             KillPlayer(&stored_player[i]);
13056       }
13057       else if (game.no_time_limit && !game.all_players_gone)
13058       {
13059         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13060
13061         DisplayGameControlValues();
13062       }
13063     }
13064
13065     if (tape.single_step && tape.recording && !tape.pausing &&
13066         !player->programmed_action)
13067       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13068
13069     if (!player->programmed_action)
13070       CheckSaveEngineSnapshot(player);
13071   }
13072 }
13073
13074 void ScrollScreen(struct PlayerInfo *player, int mode)
13075 {
13076   static unsigned int screen_frame_counter = 0;
13077
13078   if (mode == SCROLL_INIT)
13079   {
13080     // set scrolling step size according to actual player's moving speed
13081     ScrollStepSize = TILEX / player->move_delay_value;
13082
13083     screen_frame_counter = FrameCounter;
13084     ScreenMovDir = player->MovDir;
13085     ScreenMovPos = player->MovPos;
13086     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13087     return;
13088   }
13089   else if (!FrameReached(&screen_frame_counter, 1))
13090     return;
13091
13092   if (ScreenMovPos)
13093   {
13094     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13095     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13096     redraw_mask |= REDRAW_FIELD;
13097   }
13098   else
13099     ScreenMovDir = MV_NONE;
13100 }
13101
13102 void TestIfPlayerTouchesCustomElement(int x, int y)
13103 {
13104   static int xy[4][2] =
13105   {
13106     { 0, -1 },
13107     { -1, 0 },
13108     { +1, 0 },
13109     { 0, +1 }
13110   };
13111   static int trigger_sides[4][2] =
13112   {
13113     // center side       border side
13114     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13115     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13116     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13117     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13118   };
13119   static int touch_dir[4] =
13120   {
13121     MV_LEFT | MV_RIGHT,
13122     MV_UP   | MV_DOWN,
13123     MV_UP   | MV_DOWN,
13124     MV_LEFT | MV_RIGHT
13125   };
13126   int center_element = Tile[x][y];      // should always be non-moving!
13127   int i;
13128
13129   for (i = 0; i < NUM_DIRECTIONS; i++)
13130   {
13131     int xx = x + xy[i][0];
13132     int yy = y + xy[i][1];
13133     int center_side = trigger_sides[i][0];
13134     int border_side = trigger_sides[i][1];
13135     int border_element;
13136
13137     if (!IN_LEV_FIELD(xx, yy))
13138       continue;
13139
13140     if (IS_PLAYER(x, y))                // player found at center element
13141     {
13142       struct PlayerInfo *player = PLAYERINFO(x, y);
13143
13144       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13145         border_element = Tile[xx][yy];          // may be moving!
13146       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13147         border_element = Tile[xx][yy];
13148       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13149         border_element = MovingOrBlocked2Element(xx, yy);
13150       else
13151         continue;               // center and border element do not touch
13152
13153       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13154                                  player->index_bit, border_side);
13155       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13156                                           CE_PLAYER_TOUCHES_X,
13157                                           player->index_bit, border_side);
13158
13159       {
13160         /* use player element that is initially defined in the level playfield,
13161            not the player element that corresponds to the runtime player number
13162            (example: a level that contains EL_PLAYER_3 as the only player would
13163            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13164         int player_element = PLAYERINFO(x, y)->initial_element;
13165
13166         CheckElementChangeBySide(xx, yy, border_element, player_element,
13167                                  CE_TOUCHING_X, border_side);
13168       }
13169     }
13170     else if (IS_PLAYER(xx, yy))         // player found at border element
13171     {
13172       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13173
13174       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13175       {
13176         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13177           continue;             // center and border element do not touch
13178       }
13179
13180       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13181                                  player->index_bit, center_side);
13182       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13183                                           CE_PLAYER_TOUCHES_X,
13184                                           player->index_bit, center_side);
13185
13186       {
13187         /* use player element that is initially defined in the level playfield,
13188            not the player element that corresponds to the runtime player number
13189            (example: a level that contains EL_PLAYER_3 as the only player would
13190            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13191         int player_element = PLAYERINFO(xx, yy)->initial_element;
13192
13193         CheckElementChangeBySide(x, y, center_element, player_element,
13194                                  CE_TOUCHING_X, center_side);
13195       }
13196
13197       break;
13198     }
13199   }
13200 }
13201
13202 void TestIfElementTouchesCustomElement(int x, int y)
13203 {
13204   static int xy[4][2] =
13205   {
13206     { 0, -1 },
13207     { -1, 0 },
13208     { +1, 0 },
13209     { 0, +1 }
13210   };
13211   static int trigger_sides[4][2] =
13212   {
13213     // center side      border side
13214     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13215     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13216     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13217     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13218   };
13219   static int touch_dir[4] =
13220   {
13221     MV_LEFT | MV_RIGHT,
13222     MV_UP   | MV_DOWN,
13223     MV_UP   | MV_DOWN,
13224     MV_LEFT | MV_RIGHT
13225   };
13226   boolean change_center_element = FALSE;
13227   int center_element = Tile[x][y];      // should always be non-moving!
13228   int border_element_old[NUM_DIRECTIONS];
13229   int i;
13230
13231   for (i = 0; i < NUM_DIRECTIONS; i++)
13232   {
13233     int xx = x + xy[i][0];
13234     int yy = y + xy[i][1];
13235     int border_element;
13236
13237     border_element_old[i] = -1;
13238
13239     if (!IN_LEV_FIELD(xx, yy))
13240       continue;
13241
13242     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13243       border_element = Tile[xx][yy];    // may be moving!
13244     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13245       border_element = Tile[xx][yy];
13246     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13247       border_element = MovingOrBlocked2Element(xx, yy);
13248     else
13249       continue;                 // center and border element do not touch
13250
13251     border_element_old[i] = border_element;
13252   }
13253
13254   for (i = 0; i < NUM_DIRECTIONS; i++)
13255   {
13256     int xx = x + xy[i][0];
13257     int yy = y + xy[i][1];
13258     int center_side = trigger_sides[i][0];
13259     int border_element = border_element_old[i];
13260
13261     if (border_element == -1)
13262       continue;
13263
13264     // check for change of border element
13265     CheckElementChangeBySide(xx, yy, border_element, center_element,
13266                              CE_TOUCHING_X, center_side);
13267
13268     // (center element cannot be player, so we dont have to check this here)
13269   }
13270
13271   for (i = 0; i < NUM_DIRECTIONS; i++)
13272   {
13273     int xx = x + xy[i][0];
13274     int yy = y + xy[i][1];
13275     int border_side = trigger_sides[i][1];
13276     int border_element = border_element_old[i];
13277
13278     if (border_element == -1)
13279       continue;
13280
13281     // check for change of center element (but change it only once)
13282     if (!change_center_element)
13283       change_center_element =
13284         CheckElementChangeBySide(x, y, center_element, border_element,
13285                                  CE_TOUCHING_X, border_side);
13286
13287     if (IS_PLAYER(xx, yy))
13288     {
13289       /* use player element that is initially defined in the level playfield,
13290          not the player element that corresponds to the runtime player number
13291          (example: a level that contains EL_PLAYER_3 as the only player would
13292          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13293       int player_element = PLAYERINFO(xx, yy)->initial_element;
13294
13295       CheckElementChangeBySide(x, y, center_element, player_element,
13296                                CE_TOUCHING_X, border_side);
13297     }
13298   }
13299 }
13300
13301 void TestIfElementHitsCustomElement(int x, int y, int direction)
13302 {
13303   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13304   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13305   int hitx = x + dx, hity = y + dy;
13306   int hitting_element = Tile[x][y];
13307   int touched_element;
13308
13309   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13310     return;
13311
13312   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13313                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13314
13315   if (IN_LEV_FIELD(hitx, hity))
13316   {
13317     int opposite_direction = MV_DIR_OPPOSITE(direction);
13318     int hitting_side = direction;
13319     int touched_side = opposite_direction;
13320     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13321                           MovDir[hitx][hity] != direction ||
13322                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13323
13324     object_hit = TRUE;
13325
13326     if (object_hit)
13327     {
13328       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13329                                CE_HITTING_X, touched_side);
13330
13331       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13332                                CE_HIT_BY_X, hitting_side);
13333
13334       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13335                                CE_HIT_BY_SOMETHING, opposite_direction);
13336
13337       if (IS_PLAYER(hitx, hity))
13338       {
13339         /* use player element that is initially defined in the level playfield,
13340            not the player element that corresponds to the runtime player number
13341            (example: a level that contains EL_PLAYER_3 as the only player would
13342            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13343         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13344
13345         CheckElementChangeBySide(x, y, hitting_element, player_element,
13346                                  CE_HITTING_X, touched_side);
13347       }
13348     }
13349   }
13350
13351   // "hitting something" is also true when hitting the playfield border
13352   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13353                            CE_HITTING_SOMETHING, direction);
13354 }
13355
13356 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13357 {
13358   int i, kill_x = -1, kill_y = -1;
13359
13360   int bad_element = -1;
13361   static int test_xy[4][2] =
13362   {
13363     { 0, -1 },
13364     { -1, 0 },
13365     { +1, 0 },
13366     { 0, +1 }
13367   };
13368   static int test_dir[4] =
13369   {
13370     MV_UP,
13371     MV_LEFT,
13372     MV_RIGHT,
13373     MV_DOWN
13374   };
13375
13376   for (i = 0; i < NUM_DIRECTIONS; i++)
13377   {
13378     int test_x, test_y, test_move_dir, test_element;
13379
13380     test_x = good_x + test_xy[i][0];
13381     test_y = good_y + test_xy[i][1];
13382
13383     if (!IN_LEV_FIELD(test_x, test_y))
13384       continue;
13385
13386     test_move_dir =
13387       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13388
13389     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13390
13391     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13392        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13393     */
13394     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13395         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13396     {
13397       kill_x = test_x;
13398       kill_y = test_y;
13399       bad_element = test_element;
13400
13401       break;
13402     }
13403   }
13404
13405   if (kill_x != -1 || kill_y != -1)
13406   {
13407     if (IS_PLAYER(good_x, good_y))
13408     {
13409       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13410
13411       if (player->shield_deadly_time_left > 0 &&
13412           !IS_INDESTRUCTIBLE(bad_element))
13413         Bang(kill_x, kill_y);
13414       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13415         KillPlayer(player);
13416     }
13417     else
13418       Bang(good_x, good_y);
13419   }
13420 }
13421
13422 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13423 {
13424   int i, kill_x = -1, kill_y = -1;
13425   int bad_element = Tile[bad_x][bad_y];
13426   static int test_xy[4][2] =
13427   {
13428     { 0, -1 },
13429     { -1, 0 },
13430     { +1, 0 },
13431     { 0, +1 }
13432   };
13433   static int touch_dir[4] =
13434   {
13435     MV_LEFT | MV_RIGHT,
13436     MV_UP   | MV_DOWN,
13437     MV_UP   | MV_DOWN,
13438     MV_LEFT | MV_RIGHT
13439   };
13440   static int test_dir[4] =
13441   {
13442     MV_UP,
13443     MV_LEFT,
13444     MV_RIGHT,
13445     MV_DOWN
13446   };
13447
13448   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13449     return;
13450
13451   for (i = 0; i < NUM_DIRECTIONS; i++)
13452   {
13453     int test_x, test_y, test_move_dir, test_element;
13454
13455     test_x = bad_x + test_xy[i][0];
13456     test_y = bad_y + test_xy[i][1];
13457
13458     if (!IN_LEV_FIELD(test_x, test_y))
13459       continue;
13460
13461     test_move_dir =
13462       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13463
13464     test_element = Tile[test_x][test_y];
13465
13466     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13467        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13468     */
13469     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13470         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13471     {
13472       // good thing is player or penguin that does not move away
13473       if (IS_PLAYER(test_x, test_y))
13474       {
13475         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13476
13477         if (bad_element == EL_ROBOT && player->is_moving)
13478           continue;     // robot does not kill player if he is moving
13479
13480         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13481         {
13482           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13483             continue;           // center and border element do not touch
13484         }
13485
13486         kill_x = test_x;
13487         kill_y = test_y;
13488
13489         break;
13490       }
13491       else if (test_element == EL_PENGUIN)
13492       {
13493         kill_x = test_x;
13494         kill_y = test_y;
13495
13496         break;
13497       }
13498     }
13499   }
13500
13501   if (kill_x != -1 || kill_y != -1)
13502   {
13503     if (IS_PLAYER(kill_x, kill_y))
13504     {
13505       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13506
13507       if (player->shield_deadly_time_left > 0 &&
13508           !IS_INDESTRUCTIBLE(bad_element))
13509         Bang(bad_x, bad_y);
13510       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13511         KillPlayer(player);
13512     }
13513     else
13514       Bang(kill_x, kill_y);
13515   }
13516 }
13517
13518 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13519 {
13520   int bad_element = Tile[bad_x][bad_y];
13521   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13522   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13523   int test_x = bad_x + dx, test_y = bad_y + dy;
13524   int test_move_dir, test_element;
13525   int kill_x = -1, kill_y = -1;
13526
13527   if (!IN_LEV_FIELD(test_x, test_y))
13528     return;
13529
13530   test_move_dir =
13531     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13532
13533   test_element = Tile[test_x][test_y];
13534
13535   if (test_move_dir != bad_move_dir)
13536   {
13537     // good thing can be player or penguin that does not move away
13538     if (IS_PLAYER(test_x, test_y))
13539     {
13540       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13541
13542       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13543          player as being hit when he is moving towards the bad thing, because
13544          the "get hit by" condition would be lost after the player stops) */
13545       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13546         return;         // player moves away from bad thing
13547
13548       kill_x = test_x;
13549       kill_y = test_y;
13550     }
13551     else if (test_element == EL_PENGUIN)
13552     {
13553       kill_x = test_x;
13554       kill_y = test_y;
13555     }
13556   }
13557
13558   if (kill_x != -1 || kill_y != -1)
13559   {
13560     if (IS_PLAYER(kill_x, kill_y))
13561     {
13562       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13563
13564       if (player->shield_deadly_time_left > 0 &&
13565           !IS_INDESTRUCTIBLE(bad_element))
13566         Bang(bad_x, bad_y);
13567       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13568         KillPlayer(player);
13569     }
13570     else
13571       Bang(kill_x, kill_y);
13572   }
13573 }
13574
13575 void TestIfPlayerTouchesBadThing(int x, int y)
13576 {
13577   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13578 }
13579
13580 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13581 {
13582   TestIfGoodThingHitsBadThing(x, y, move_dir);
13583 }
13584
13585 void TestIfBadThingTouchesPlayer(int x, int y)
13586 {
13587   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13588 }
13589
13590 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13591 {
13592   TestIfBadThingHitsGoodThing(x, y, move_dir);
13593 }
13594
13595 void TestIfFriendTouchesBadThing(int x, int y)
13596 {
13597   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13598 }
13599
13600 void TestIfBadThingTouchesFriend(int x, int y)
13601 {
13602   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13603 }
13604
13605 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13606 {
13607   int i, kill_x = bad_x, kill_y = bad_y;
13608   static int xy[4][2] =
13609   {
13610     { 0, -1 },
13611     { -1, 0 },
13612     { +1, 0 },
13613     { 0, +1 }
13614   };
13615
13616   for (i = 0; i < NUM_DIRECTIONS; i++)
13617   {
13618     int x, y, element;
13619
13620     x = bad_x + xy[i][0];
13621     y = bad_y + xy[i][1];
13622     if (!IN_LEV_FIELD(x, y))
13623       continue;
13624
13625     element = Tile[x][y];
13626     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13627         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13628     {
13629       kill_x = x;
13630       kill_y = y;
13631       break;
13632     }
13633   }
13634
13635   if (kill_x != bad_x || kill_y != bad_y)
13636     Bang(bad_x, bad_y);
13637 }
13638
13639 void KillPlayer(struct PlayerInfo *player)
13640 {
13641   int jx = player->jx, jy = player->jy;
13642
13643   if (!player->active)
13644     return;
13645
13646 #if 0
13647   Debug("game:playing:KillPlayer",
13648         "0: killed == %d, active == %d, reanimated == %d",
13649         player->killed, player->active, player->reanimated);
13650 #endif
13651
13652   /* the following code was introduced to prevent an infinite loop when calling
13653      -> Bang()
13654      -> CheckTriggeredElementChangeExt()
13655      -> ExecuteCustomElementAction()
13656      -> KillPlayer()
13657      -> (infinitely repeating the above sequence of function calls)
13658      which occurs when killing the player while having a CE with the setting
13659      "kill player X when explosion of <player X>"; the solution using a new
13660      field "player->killed" was chosen for backwards compatibility, although
13661      clever use of the fields "player->active" etc. would probably also work */
13662 #if 1
13663   if (player->killed)
13664     return;
13665 #endif
13666
13667   player->killed = TRUE;
13668
13669   // remove accessible field at the player's position
13670   Tile[jx][jy] = EL_EMPTY;
13671
13672   // deactivate shield (else Bang()/Explode() would not work right)
13673   player->shield_normal_time_left = 0;
13674   player->shield_deadly_time_left = 0;
13675
13676 #if 0
13677   Debug("game:playing:KillPlayer",
13678         "1: killed == %d, active == %d, reanimated == %d",
13679         player->killed, player->active, player->reanimated);
13680 #endif
13681
13682   Bang(jx, jy);
13683
13684 #if 0
13685   Debug("game:playing:KillPlayer",
13686         "2: killed == %d, active == %d, reanimated == %d",
13687         player->killed, player->active, player->reanimated);
13688 #endif
13689
13690   if (player->reanimated)       // killed player may have been reanimated
13691     player->killed = player->reanimated = FALSE;
13692   else
13693     BuryPlayer(player);
13694 }
13695
13696 static void KillPlayerUnlessEnemyProtected(int x, int y)
13697 {
13698   if (!PLAYER_ENEMY_PROTECTED(x, y))
13699     KillPlayer(PLAYERINFO(x, y));
13700 }
13701
13702 static void KillPlayerUnlessExplosionProtected(int x, int y)
13703 {
13704   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13705     KillPlayer(PLAYERINFO(x, y));
13706 }
13707
13708 void BuryPlayer(struct PlayerInfo *player)
13709 {
13710   int jx = player->jx, jy = player->jy;
13711
13712   if (!player->active)
13713     return;
13714
13715   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13716   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13717
13718   RemovePlayer(player);
13719
13720   player->buried = TRUE;
13721
13722   if (game.all_players_gone)
13723     game.GameOver = TRUE;
13724 }
13725
13726 void RemovePlayer(struct PlayerInfo *player)
13727 {
13728   int jx = player->jx, jy = player->jy;
13729   int i, found = FALSE;
13730
13731   player->present = FALSE;
13732   player->active = FALSE;
13733
13734   // required for some CE actions (even if the player is not active anymore)
13735   player->MovPos = 0;
13736
13737   if (!ExplodeField[jx][jy])
13738     StorePlayer[jx][jy] = 0;
13739
13740   if (player->is_moving)
13741     TEST_DrawLevelField(player->last_jx, player->last_jy);
13742
13743   for (i = 0; i < MAX_PLAYERS; i++)
13744     if (stored_player[i].active)
13745       found = TRUE;
13746
13747   if (!found)
13748   {
13749     game.all_players_gone = TRUE;
13750     game.GameOver = TRUE;
13751   }
13752
13753   game.exit_x = game.robot_wheel_x = jx;
13754   game.exit_y = game.robot_wheel_y = jy;
13755 }
13756
13757 void ExitPlayer(struct PlayerInfo *player)
13758 {
13759   DrawPlayer(player);   // needed here only to cleanup last field
13760   RemovePlayer(player);
13761
13762   if (game.players_still_needed > 0)
13763     game.players_still_needed--;
13764 }
13765
13766 static void setFieldForSnapping(int x, int y, int element, int direction)
13767 {
13768   struct ElementInfo *ei = &element_info[element];
13769   int direction_bit = MV_DIR_TO_BIT(direction);
13770   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13771   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13772                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13773
13774   Tile[x][y] = EL_ELEMENT_SNAPPING;
13775   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13776
13777   ResetGfxAnimation(x, y);
13778
13779   GfxElement[x][y] = element;
13780   GfxAction[x][y] = action;
13781   GfxDir[x][y] = direction;
13782   GfxFrame[x][y] = -1;
13783 }
13784
13785 /*
13786   =============================================================================
13787   checkDiagonalPushing()
13788   -----------------------------------------------------------------------------
13789   check if diagonal input device direction results in pushing of object
13790   (by checking if the alternative direction is walkable, diggable, ...)
13791   =============================================================================
13792 */
13793
13794 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13795                                     int x, int y, int real_dx, int real_dy)
13796 {
13797   int jx, jy, dx, dy, xx, yy;
13798
13799   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13800     return TRUE;
13801
13802   // diagonal direction: check alternative direction
13803   jx = player->jx;
13804   jy = player->jy;
13805   dx = x - jx;
13806   dy = y - jy;
13807   xx = jx + (dx == 0 ? real_dx : 0);
13808   yy = jy + (dy == 0 ? real_dy : 0);
13809
13810   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
13811 }
13812
13813 /*
13814   =============================================================================
13815   DigField()
13816   -----------------------------------------------------------------------------
13817   x, y:                 field next to player (non-diagonal) to try to dig to
13818   real_dx, real_dy:     direction as read from input device (can be diagonal)
13819   =============================================================================
13820 */
13821
13822 static int DigField(struct PlayerInfo *player,
13823                     int oldx, int oldy, int x, int y,
13824                     int real_dx, int real_dy, int mode)
13825 {
13826   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13827   boolean player_was_pushing = player->is_pushing;
13828   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13829   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13830   int jx = oldx, jy = oldy;
13831   int dx = x - jx, dy = y - jy;
13832   int nextx = x + dx, nexty = y + dy;
13833   int move_direction = (dx == -1 ? MV_LEFT  :
13834                         dx == +1 ? MV_RIGHT :
13835                         dy == -1 ? MV_UP    :
13836                         dy == +1 ? MV_DOWN  : MV_NONE);
13837   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13838   int dig_side = MV_DIR_OPPOSITE(move_direction);
13839   int old_element = Tile[jx][jy];
13840   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13841   int collect_count;
13842
13843   if (is_player)                // function can also be called by EL_PENGUIN
13844   {
13845     if (player->MovPos == 0)
13846     {
13847       player->is_digging = FALSE;
13848       player->is_collecting = FALSE;
13849     }
13850
13851     if (player->MovPos == 0)    // last pushing move finished
13852       player->is_pushing = FALSE;
13853
13854     if (mode == DF_NO_PUSH)     // player just stopped pushing
13855     {
13856       player->is_switching = FALSE;
13857       player->push_delay = -1;
13858
13859       return MP_NO_ACTION;
13860     }
13861   }
13862
13863   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13864     old_element = Back[jx][jy];
13865
13866   // in case of element dropped at player position, check background
13867   else if (Back[jx][jy] != EL_EMPTY &&
13868            game.engine_version >= VERSION_IDENT(2,2,0,0))
13869     old_element = Back[jx][jy];
13870
13871   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13872     return MP_NO_ACTION;        // field has no opening in this direction
13873
13874   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13875     return MP_NO_ACTION;        // field has no opening in this direction
13876
13877   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13878   {
13879     SplashAcid(x, y);
13880
13881     Tile[jx][jy] = player->artwork_element;
13882     InitMovingField(jx, jy, MV_DOWN);
13883     Store[jx][jy] = EL_ACID;
13884     ContinueMoving(jx, jy);
13885     BuryPlayer(player);
13886
13887     return MP_DONT_RUN_INTO;
13888   }
13889
13890   if (player_can_move && DONT_RUN_INTO(element))
13891   {
13892     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13893
13894     return MP_DONT_RUN_INTO;
13895   }
13896
13897   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13898     return MP_NO_ACTION;
13899
13900   collect_count = element_info[element].collect_count_initial;
13901
13902   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
13903     return MP_NO_ACTION;
13904
13905   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13906     player_can_move = player_can_move_or_snap;
13907
13908   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13909       game.engine_version >= VERSION_IDENT(2,2,0,0))
13910   {
13911     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13912                                player->index_bit, dig_side);
13913     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13914                                         player->index_bit, dig_side);
13915
13916     if (element == EL_DC_LANDMINE)
13917       Bang(x, y);
13918
13919     if (Tile[x][y] != element)          // field changed by snapping
13920       return MP_ACTION;
13921
13922     return MP_NO_ACTION;
13923   }
13924
13925   if (player->gravity && is_player && !player->is_auto_moving &&
13926       canFallDown(player) && move_direction != MV_DOWN &&
13927       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13928     return MP_NO_ACTION;        // player cannot walk here due to gravity
13929
13930   if (player_can_move &&
13931       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13932   {
13933     int sound_element = SND_ELEMENT(element);
13934     int sound_action = ACTION_WALKING;
13935
13936     if (IS_RND_GATE(element))
13937     {
13938       if (!player->key[RND_GATE_NR(element)])
13939         return MP_NO_ACTION;
13940     }
13941     else if (IS_RND_GATE_GRAY(element))
13942     {
13943       if (!player->key[RND_GATE_GRAY_NR(element)])
13944         return MP_NO_ACTION;
13945     }
13946     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13947     {
13948       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13949         return MP_NO_ACTION;
13950     }
13951     else if (element == EL_EXIT_OPEN ||
13952              element == EL_EM_EXIT_OPEN ||
13953              element == EL_EM_EXIT_OPENING ||
13954              element == EL_STEEL_EXIT_OPEN ||
13955              element == EL_EM_STEEL_EXIT_OPEN ||
13956              element == EL_EM_STEEL_EXIT_OPENING ||
13957              element == EL_SP_EXIT_OPEN ||
13958              element == EL_SP_EXIT_OPENING)
13959     {
13960       sound_action = ACTION_PASSING;    // player is passing exit
13961     }
13962     else if (element == EL_EMPTY)
13963     {
13964       sound_action = ACTION_MOVING;             // nothing to walk on
13965     }
13966
13967     // play sound from background or player, whatever is available
13968     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13969       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13970     else
13971       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13972   }
13973   else if (player_can_move &&
13974            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13975   {
13976     if (!ACCESS_FROM(element, opposite_direction))
13977       return MP_NO_ACTION;      // field not accessible from this direction
13978
13979     if (CAN_MOVE(element))      // only fixed elements can be passed!
13980       return MP_NO_ACTION;
13981
13982     if (IS_EM_GATE(element))
13983     {
13984       if (!player->key[EM_GATE_NR(element)])
13985         return MP_NO_ACTION;
13986     }
13987     else if (IS_EM_GATE_GRAY(element))
13988     {
13989       if (!player->key[EM_GATE_GRAY_NR(element)])
13990         return MP_NO_ACTION;
13991     }
13992     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13993     {
13994       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13995         return MP_NO_ACTION;
13996     }
13997     else if (IS_EMC_GATE(element))
13998     {
13999       if (!player->key[EMC_GATE_NR(element)])
14000         return MP_NO_ACTION;
14001     }
14002     else if (IS_EMC_GATE_GRAY(element))
14003     {
14004       if (!player->key[EMC_GATE_GRAY_NR(element)])
14005         return MP_NO_ACTION;
14006     }
14007     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14008     {
14009       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14010         return MP_NO_ACTION;
14011     }
14012     else if (element == EL_DC_GATE_WHITE ||
14013              element == EL_DC_GATE_WHITE_GRAY ||
14014              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14015     {
14016       if (player->num_white_keys == 0)
14017         return MP_NO_ACTION;
14018
14019       player->num_white_keys--;
14020     }
14021     else if (IS_SP_PORT(element))
14022     {
14023       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14024           element == EL_SP_GRAVITY_PORT_RIGHT ||
14025           element == EL_SP_GRAVITY_PORT_UP ||
14026           element == EL_SP_GRAVITY_PORT_DOWN)
14027         player->gravity = !player->gravity;
14028       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14029                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14030                element == EL_SP_GRAVITY_ON_PORT_UP ||
14031                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14032         player->gravity = TRUE;
14033       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14034                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14035                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14036                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14037         player->gravity = FALSE;
14038     }
14039
14040     // automatically move to the next field with double speed
14041     player->programmed_action = move_direction;
14042
14043     if (player->move_delay_reset_counter == 0)
14044     {
14045       player->move_delay_reset_counter = 2;     // two double speed steps
14046
14047       DOUBLE_PLAYER_SPEED(player);
14048     }
14049
14050     PlayLevelSoundAction(x, y, ACTION_PASSING);
14051   }
14052   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14053   {
14054     RemoveField(x, y);
14055
14056     if (mode != DF_SNAP)
14057     {
14058       GfxElement[x][y] = GFX_ELEMENT(element);
14059       player->is_digging = TRUE;
14060     }
14061
14062     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14063
14064     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14065                                         player->index_bit, dig_side);
14066
14067     // if digging triggered player relocation, finish digging tile
14068     if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14069       setFieldForSnapping(x, y, element, move_direction);
14070
14071     if (mode == DF_SNAP)
14072     {
14073       if (level.block_snap_field)
14074         setFieldForSnapping(x, y, element, move_direction);
14075       else
14076         TestIfElementTouchesCustomElement(x, y);        // for empty space
14077
14078       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14079                                           player->index_bit, dig_side);
14080     }
14081   }
14082   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14083   {
14084     RemoveField(x, y);
14085
14086     if (is_player && mode != DF_SNAP)
14087     {
14088       GfxElement[x][y] = element;
14089       player->is_collecting = TRUE;
14090     }
14091
14092     if (element == EL_SPEED_PILL)
14093     {
14094       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14095     }
14096     else if (element == EL_EXTRA_TIME && level.time > 0)
14097     {
14098       TimeLeft += level.extra_time;
14099
14100       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14101
14102       DisplayGameControlValues();
14103     }
14104     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14105     {
14106       player->shield_normal_time_left += level.shield_normal_time;
14107       if (element == EL_SHIELD_DEADLY)
14108         player->shield_deadly_time_left += level.shield_deadly_time;
14109     }
14110     else if (element == EL_DYNAMITE ||
14111              element == EL_EM_DYNAMITE ||
14112              element == EL_SP_DISK_RED)
14113     {
14114       if (player->inventory_size < MAX_INVENTORY_SIZE)
14115         player->inventory_element[player->inventory_size++] = element;
14116
14117       DrawGameDoorValues();
14118     }
14119     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14120     {
14121       player->dynabomb_count++;
14122       player->dynabombs_left++;
14123     }
14124     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14125     {
14126       player->dynabomb_size++;
14127     }
14128     else if (element == EL_DYNABOMB_INCREASE_POWER)
14129     {
14130       player->dynabomb_xl = TRUE;
14131     }
14132     else if (IS_KEY(element))
14133     {
14134       player->key[KEY_NR(element)] = TRUE;
14135
14136       DrawGameDoorValues();
14137     }
14138     else if (element == EL_DC_KEY_WHITE)
14139     {
14140       player->num_white_keys++;
14141
14142       // display white keys?
14143       // DrawGameDoorValues();
14144     }
14145     else if (IS_ENVELOPE(element))
14146     {
14147       player->show_envelope = element;
14148     }
14149     else if (element == EL_EMC_LENSES)
14150     {
14151       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14152
14153       RedrawAllInvisibleElementsForLenses();
14154     }
14155     else if (element == EL_EMC_MAGNIFIER)
14156     {
14157       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14158
14159       RedrawAllInvisibleElementsForMagnifier();
14160     }
14161     else if (IS_DROPPABLE(element) ||
14162              IS_THROWABLE(element))     // can be collected and dropped
14163     {
14164       int i;
14165
14166       if (collect_count == 0)
14167         player->inventory_infinite_element = element;
14168       else
14169         for (i = 0; i < collect_count; i++)
14170           if (player->inventory_size < MAX_INVENTORY_SIZE)
14171             player->inventory_element[player->inventory_size++] = element;
14172
14173       DrawGameDoorValues();
14174     }
14175     else if (collect_count > 0)
14176     {
14177       game.gems_still_needed -= collect_count;
14178       if (game.gems_still_needed < 0)
14179         game.gems_still_needed = 0;
14180
14181       game.snapshot.collected_item = TRUE;
14182
14183       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14184
14185       DisplayGameControlValues();
14186     }
14187
14188     RaiseScoreElement(element);
14189     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14190
14191     if (is_player)
14192     {
14193       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14194                                           player->index_bit, dig_side);
14195
14196       // if collecting triggered player relocation, finish collecting tile
14197       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14198         setFieldForSnapping(x, y, element, move_direction);
14199     }
14200
14201     if (mode == DF_SNAP)
14202     {
14203       if (level.block_snap_field)
14204         setFieldForSnapping(x, y, element, move_direction);
14205       else
14206         TestIfElementTouchesCustomElement(x, y);        // for empty space
14207
14208       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14209                                           player->index_bit, dig_side);
14210     }
14211   }
14212   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14213   {
14214     if (mode == DF_SNAP && element != EL_BD_ROCK)
14215       return MP_NO_ACTION;
14216
14217     if (CAN_FALL(element) && dy)
14218       return MP_NO_ACTION;
14219
14220     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14221         !(element == EL_SPRING && level.use_spring_bug))
14222       return MP_NO_ACTION;
14223
14224     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14225         ((move_direction & MV_VERTICAL &&
14226           ((element_info[element].move_pattern & MV_LEFT &&
14227             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14228            (element_info[element].move_pattern & MV_RIGHT &&
14229             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14230          (move_direction & MV_HORIZONTAL &&
14231           ((element_info[element].move_pattern & MV_UP &&
14232             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14233            (element_info[element].move_pattern & MV_DOWN &&
14234             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14235       return MP_NO_ACTION;
14236
14237     // do not push elements already moving away faster than player
14238     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14239         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14240       return MP_NO_ACTION;
14241
14242     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14243     {
14244       if (player->push_delay_value == -1 || !player_was_pushing)
14245         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14246     }
14247     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14248     {
14249       if (player->push_delay_value == -1)
14250         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14251     }
14252     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14253     {
14254       if (!player->is_pushing)
14255         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14256     }
14257
14258     player->is_pushing = TRUE;
14259     player->is_active = TRUE;
14260
14261     if (!(IN_LEV_FIELD(nextx, nexty) &&
14262           (IS_FREE(nextx, nexty) ||
14263            (IS_SB_ELEMENT(element) &&
14264             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14265            (IS_CUSTOM_ELEMENT(element) &&
14266             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14267       return MP_NO_ACTION;
14268
14269     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14270       return MP_NO_ACTION;
14271
14272     if (player->push_delay == -1)       // new pushing; restart delay
14273       player->push_delay = 0;
14274
14275     if (player->push_delay < player->push_delay_value &&
14276         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14277         element != EL_SPRING && element != EL_BALLOON)
14278     {
14279       // make sure that there is no move delay before next try to push
14280       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14281         player->move_delay = 0;
14282
14283       return MP_NO_ACTION;
14284     }
14285
14286     if (IS_CUSTOM_ELEMENT(element) &&
14287         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14288     {
14289       if (!DigFieldByCE(nextx, nexty, element))
14290         return MP_NO_ACTION;
14291     }
14292
14293     if (IS_SB_ELEMENT(element))
14294     {
14295       boolean sokoban_task_solved = FALSE;
14296
14297       if (element == EL_SOKOBAN_FIELD_FULL)
14298       {
14299         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14300
14301         IncrementSokobanFieldsNeeded();
14302         IncrementSokobanObjectsNeeded();
14303       }
14304
14305       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14306       {
14307         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14308
14309         DecrementSokobanFieldsNeeded();
14310         DecrementSokobanObjectsNeeded();
14311
14312         // sokoban object was pushed from empty field to sokoban field
14313         if (Back[x][y] == EL_EMPTY)
14314           sokoban_task_solved = TRUE;
14315       }
14316
14317       Tile[x][y] = EL_SOKOBAN_OBJECT;
14318
14319       if (Back[x][y] == Back[nextx][nexty])
14320         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14321       else if (Back[x][y] != 0)
14322         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14323                                     ACTION_EMPTYING);
14324       else
14325         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14326                                     ACTION_FILLING);
14327
14328       if (sokoban_task_solved &&
14329           game.sokoban_fields_still_needed == 0 &&
14330           game.sokoban_objects_still_needed == 0 &&
14331           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14332       {
14333         game.players_still_needed = 0;
14334
14335         LevelSolved();
14336
14337         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14338       }
14339     }
14340     else
14341       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14342
14343     InitMovingField(x, y, move_direction);
14344     GfxAction[x][y] = ACTION_PUSHING;
14345
14346     if (mode == DF_SNAP)
14347       ContinueMoving(x, y);
14348     else
14349       MovPos[x][y] = (dx != 0 ? dx : dy);
14350
14351     Pushed[x][y] = TRUE;
14352     Pushed[nextx][nexty] = TRUE;
14353
14354     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14355       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14356     else
14357       player->push_delay_value = -1;    // get new value later
14358
14359     // check for element change _after_ element has been pushed
14360     if (game.use_change_when_pushing_bug)
14361     {
14362       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14363                                  player->index_bit, dig_side);
14364       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14365                                           player->index_bit, dig_side);
14366     }
14367   }
14368   else if (IS_SWITCHABLE(element))
14369   {
14370     if (PLAYER_SWITCHING(player, x, y))
14371     {
14372       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14373                                           player->index_bit, dig_side);
14374
14375       return MP_ACTION;
14376     }
14377
14378     player->is_switching = TRUE;
14379     player->switch_x = x;
14380     player->switch_y = y;
14381
14382     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14383
14384     if (element == EL_ROBOT_WHEEL)
14385     {
14386       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14387
14388       game.robot_wheel_x = x;
14389       game.robot_wheel_y = y;
14390       game.robot_wheel_active = TRUE;
14391
14392       TEST_DrawLevelField(x, y);
14393     }
14394     else if (element == EL_SP_TERMINAL)
14395     {
14396       int xx, yy;
14397
14398       SCAN_PLAYFIELD(xx, yy)
14399       {
14400         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14401         {
14402           Bang(xx, yy);
14403         }
14404         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14405         {
14406           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14407
14408           ResetGfxAnimation(xx, yy);
14409           TEST_DrawLevelField(xx, yy);
14410         }
14411       }
14412     }
14413     else if (IS_BELT_SWITCH(element))
14414     {
14415       ToggleBeltSwitch(x, y);
14416     }
14417     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14418              element == EL_SWITCHGATE_SWITCH_DOWN ||
14419              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14420              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14421     {
14422       ToggleSwitchgateSwitch(x, y);
14423     }
14424     else if (element == EL_LIGHT_SWITCH ||
14425              element == EL_LIGHT_SWITCH_ACTIVE)
14426     {
14427       ToggleLightSwitch(x, y);
14428     }
14429     else if (element == EL_TIMEGATE_SWITCH ||
14430              element == EL_DC_TIMEGATE_SWITCH)
14431     {
14432       ActivateTimegateSwitch(x, y);
14433     }
14434     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14435              element == EL_BALLOON_SWITCH_RIGHT ||
14436              element == EL_BALLOON_SWITCH_UP    ||
14437              element == EL_BALLOON_SWITCH_DOWN  ||
14438              element == EL_BALLOON_SWITCH_NONE  ||
14439              element == EL_BALLOON_SWITCH_ANY)
14440     {
14441       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14442                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14443                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14444                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14445                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14446                              move_direction);
14447     }
14448     else if (element == EL_LAMP)
14449     {
14450       Tile[x][y] = EL_LAMP_ACTIVE;
14451       game.lights_still_needed--;
14452
14453       ResetGfxAnimation(x, y);
14454       TEST_DrawLevelField(x, y);
14455     }
14456     else if (element == EL_TIME_ORB_FULL)
14457     {
14458       Tile[x][y] = EL_TIME_ORB_EMPTY;
14459
14460       if (level.time > 0 || level.use_time_orb_bug)
14461       {
14462         TimeLeft += level.time_orb_time;
14463         game.no_time_limit = FALSE;
14464
14465         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14466
14467         DisplayGameControlValues();
14468       }
14469
14470       ResetGfxAnimation(x, y);
14471       TEST_DrawLevelField(x, y);
14472     }
14473     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14474              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14475     {
14476       int xx, yy;
14477
14478       game.ball_active = !game.ball_active;
14479
14480       SCAN_PLAYFIELD(xx, yy)
14481       {
14482         int e = Tile[xx][yy];
14483
14484         if (game.ball_active)
14485         {
14486           if (e == EL_EMC_MAGIC_BALL)
14487             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14488           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14489             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14490         }
14491         else
14492         {
14493           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14494             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14495           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14496             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14497         }
14498       }
14499     }
14500
14501     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14502                                         player->index_bit, dig_side);
14503
14504     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14505                                         player->index_bit, dig_side);
14506
14507     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14508                                         player->index_bit, dig_side);
14509
14510     return MP_ACTION;
14511   }
14512   else
14513   {
14514     if (!PLAYER_SWITCHING(player, x, y))
14515     {
14516       player->is_switching = TRUE;
14517       player->switch_x = x;
14518       player->switch_y = y;
14519
14520       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14521                                  player->index_bit, dig_side);
14522       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14523                                           player->index_bit, dig_side);
14524
14525       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14526                                  player->index_bit, dig_side);
14527       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14528                                           player->index_bit, dig_side);
14529     }
14530
14531     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14532                                player->index_bit, dig_side);
14533     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14534                                         player->index_bit, dig_side);
14535
14536     return MP_NO_ACTION;
14537   }
14538
14539   player->push_delay = -1;
14540
14541   if (is_player)                // function can also be called by EL_PENGUIN
14542   {
14543     if (Tile[x][y] != element)          // really digged/collected something
14544     {
14545       player->is_collecting = !player->is_digging;
14546       player->is_active = TRUE;
14547     }
14548   }
14549
14550   return MP_MOVING;
14551 }
14552
14553 static boolean DigFieldByCE(int x, int y, int digging_element)
14554 {
14555   int element = Tile[x][y];
14556
14557   if (!IS_FREE(x, y))
14558   {
14559     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14560                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14561                   ACTION_BREAKING);
14562
14563     // no element can dig solid indestructible elements
14564     if (IS_INDESTRUCTIBLE(element) &&
14565         !IS_DIGGABLE(element) &&
14566         !IS_COLLECTIBLE(element))
14567       return FALSE;
14568
14569     if (AmoebaNr[x][y] &&
14570         (element == EL_AMOEBA_FULL ||
14571          element == EL_BD_AMOEBA ||
14572          element == EL_AMOEBA_GROWING))
14573     {
14574       AmoebaCnt[AmoebaNr[x][y]]--;
14575       AmoebaCnt2[AmoebaNr[x][y]]--;
14576     }
14577
14578     if (IS_MOVING(x, y))
14579       RemoveMovingField(x, y);
14580     else
14581     {
14582       RemoveField(x, y);
14583       TEST_DrawLevelField(x, y);
14584     }
14585
14586     // if digged element was about to explode, prevent the explosion
14587     ExplodeField[x][y] = EX_TYPE_NONE;
14588
14589     PlayLevelSoundAction(x, y, action);
14590   }
14591
14592   Store[x][y] = EL_EMPTY;
14593
14594   // this makes it possible to leave the removed element again
14595   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14596     Store[x][y] = element;
14597
14598   return TRUE;
14599 }
14600
14601 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14602 {
14603   int jx = player->jx, jy = player->jy;
14604   int x = jx + dx, y = jy + dy;
14605   int snap_direction = (dx == -1 ? MV_LEFT  :
14606                         dx == +1 ? MV_RIGHT :
14607                         dy == -1 ? MV_UP    :
14608                         dy == +1 ? MV_DOWN  : MV_NONE);
14609   boolean can_continue_snapping = (level.continuous_snapping &&
14610                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14611
14612   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14613     return FALSE;
14614
14615   if (!player->active || !IN_LEV_FIELD(x, y))
14616     return FALSE;
14617
14618   if (dx && dy)
14619     return FALSE;
14620
14621   if (!dx && !dy)
14622   {
14623     if (player->MovPos == 0)
14624       player->is_pushing = FALSE;
14625
14626     player->is_snapping = FALSE;
14627
14628     if (player->MovPos == 0)
14629     {
14630       player->is_moving = FALSE;
14631       player->is_digging = FALSE;
14632       player->is_collecting = FALSE;
14633     }
14634
14635     return FALSE;
14636   }
14637
14638   // prevent snapping with already pressed snap key when not allowed
14639   if (player->is_snapping && !can_continue_snapping)
14640     return FALSE;
14641
14642   player->MovDir = snap_direction;
14643
14644   if (player->MovPos == 0)
14645   {
14646     player->is_moving = FALSE;
14647     player->is_digging = FALSE;
14648     player->is_collecting = FALSE;
14649   }
14650
14651   player->is_dropping = FALSE;
14652   player->is_dropping_pressed = FALSE;
14653   player->drop_pressed_delay = 0;
14654
14655   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14656     return FALSE;
14657
14658   player->is_snapping = TRUE;
14659   player->is_active = TRUE;
14660
14661   if (player->MovPos == 0)
14662   {
14663     player->is_moving = FALSE;
14664     player->is_digging = FALSE;
14665     player->is_collecting = FALSE;
14666   }
14667
14668   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14669     TEST_DrawLevelField(player->last_jx, player->last_jy);
14670
14671   TEST_DrawLevelField(x, y);
14672
14673   return TRUE;
14674 }
14675
14676 static boolean DropElement(struct PlayerInfo *player)
14677 {
14678   int old_element, new_element;
14679   int dropx = player->jx, dropy = player->jy;
14680   int drop_direction = player->MovDir;
14681   int drop_side = drop_direction;
14682   int drop_element = get_next_dropped_element(player);
14683
14684   /* do not drop an element on top of another element; when holding drop key
14685      pressed without moving, dropped element must move away before the next
14686      element can be dropped (this is especially important if the next element
14687      is dynamite, which can be placed on background for historical reasons) */
14688   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
14689     return MP_ACTION;
14690
14691   if (IS_THROWABLE(drop_element))
14692   {
14693     dropx += GET_DX_FROM_DIR(drop_direction);
14694     dropy += GET_DY_FROM_DIR(drop_direction);
14695
14696     if (!IN_LEV_FIELD(dropx, dropy))
14697       return FALSE;
14698   }
14699
14700   old_element = Tile[dropx][dropy];     // old element at dropping position
14701   new_element = drop_element;           // default: no change when dropping
14702
14703   // check if player is active, not moving and ready to drop
14704   if (!player->active || player->MovPos || player->drop_delay > 0)
14705     return FALSE;
14706
14707   // check if player has anything that can be dropped
14708   if (new_element == EL_UNDEFINED)
14709     return FALSE;
14710
14711   // only set if player has anything that can be dropped
14712   player->is_dropping_pressed = TRUE;
14713
14714   // check if drop key was pressed long enough for EM style dynamite
14715   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14716     return FALSE;
14717
14718   // check if anything can be dropped at the current position
14719   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14720     return FALSE;
14721
14722   // collected custom elements can only be dropped on empty fields
14723   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14724     return FALSE;
14725
14726   if (old_element != EL_EMPTY)
14727     Back[dropx][dropy] = old_element;   // store old element on this field
14728
14729   ResetGfxAnimation(dropx, dropy);
14730   ResetRandomAnimationValue(dropx, dropy);
14731
14732   if (player->inventory_size > 0 ||
14733       player->inventory_infinite_element != EL_UNDEFINED)
14734   {
14735     if (player->inventory_size > 0)
14736     {
14737       player->inventory_size--;
14738
14739       DrawGameDoorValues();
14740
14741       if (new_element == EL_DYNAMITE)
14742         new_element = EL_DYNAMITE_ACTIVE;
14743       else if (new_element == EL_EM_DYNAMITE)
14744         new_element = EL_EM_DYNAMITE_ACTIVE;
14745       else if (new_element == EL_SP_DISK_RED)
14746         new_element = EL_SP_DISK_RED_ACTIVE;
14747     }
14748
14749     Tile[dropx][dropy] = new_element;
14750
14751     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14752       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14753                           el2img(Tile[dropx][dropy]), 0);
14754
14755     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14756
14757     // needed if previous element just changed to "empty" in the last frame
14758     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14759
14760     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14761                                player->index_bit, drop_side);
14762     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14763                                         CE_PLAYER_DROPS_X,
14764                                         player->index_bit, drop_side);
14765
14766     TestIfElementTouchesCustomElement(dropx, dropy);
14767   }
14768   else          // player is dropping a dyna bomb
14769   {
14770     player->dynabombs_left--;
14771
14772     Tile[dropx][dropy] = new_element;
14773
14774     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14775       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14776                           el2img(Tile[dropx][dropy]), 0);
14777
14778     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14779   }
14780
14781   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
14782     InitField_WithBug1(dropx, dropy, FALSE);
14783
14784   new_element = Tile[dropx][dropy];     // element might have changed
14785
14786   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14787       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14788   {
14789     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14790       MovDir[dropx][dropy] = drop_direction;
14791
14792     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14793
14794     // do not cause impact style collision by dropping elements that can fall
14795     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14796   }
14797
14798   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14799   player->is_dropping = TRUE;
14800
14801   player->drop_pressed_delay = 0;
14802   player->is_dropping_pressed = FALSE;
14803
14804   player->drop_x = dropx;
14805   player->drop_y = dropy;
14806
14807   return TRUE;
14808 }
14809
14810 // ----------------------------------------------------------------------------
14811 // game sound playing functions
14812 // ----------------------------------------------------------------------------
14813
14814 static int *loop_sound_frame = NULL;
14815 static int *loop_sound_volume = NULL;
14816
14817 void InitPlayLevelSound(void)
14818 {
14819   int num_sounds = getSoundListSize();
14820
14821   checked_free(loop_sound_frame);
14822   checked_free(loop_sound_volume);
14823
14824   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14825   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14826 }
14827
14828 static void PlayLevelSound(int x, int y, int nr)
14829 {
14830   int sx = SCREENX(x), sy = SCREENY(y);
14831   int volume, stereo_position;
14832   int max_distance = 8;
14833   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14834
14835   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14836       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14837     return;
14838
14839   if (!IN_LEV_FIELD(x, y) ||
14840       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14841       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14842     return;
14843
14844   volume = SOUND_MAX_VOLUME;
14845
14846   if (!IN_SCR_FIELD(sx, sy))
14847   {
14848     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14849     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14850
14851     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14852   }
14853
14854   stereo_position = (SOUND_MAX_LEFT +
14855                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14856                      (SCR_FIELDX + 2 * max_distance));
14857
14858   if (IS_LOOP_SOUND(nr))
14859   {
14860     /* This assures that quieter loop sounds do not overwrite louder ones,
14861        while restarting sound volume comparison with each new game frame. */
14862
14863     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14864       return;
14865
14866     loop_sound_volume[nr] = volume;
14867     loop_sound_frame[nr] = FrameCounter;
14868   }
14869
14870   PlaySoundExt(nr, volume, stereo_position, type);
14871 }
14872
14873 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14874 {
14875   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14876                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14877                  y < LEVELY(BY1) ? LEVELY(BY1) :
14878                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14879                  sound_action);
14880 }
14881
14882 static void PlayLevelSoundAction(int x, int y, int action)
14883 {
14884   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
14885 }
14886
14887 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14888 {
14889   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14890
14891   if (sound_effect != SND_UNDEFINED)
14892     PlayLevelSound(x, y, sound_effect);
14893 }
14894
14895 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14896                                               int action)
14897 {
14898   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14899
14900   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14901     PlayLevelSound(x, y, sound_effect);
14902 }
14903
14904 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14905 {
14906   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
14907
14908   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14909     PlayLevelSound(x, y, sound_effect);
14910 }
14911
14912 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14913 {
14914   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
14915
14916   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14917     StopSound(sound_effect);
14918 }
14919
14920 static int getLevelMusicNr(void)
14921 {
14922   if (levelset.music[level_nr] != MUS_UNDEFINED)
14923     return levelset.music[level_nr];            // from config file
14924   else
14925     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
14926 }
14927
14928 static void FadeLevelSounds(void)
14929 {
14930   FadeSounds();
14931 }
14932
14933 static void FadeLevelMusic(void)
14934 {
14935   int music_nr = getLevelMusicNr();
14936   char *curr_music = getCurrentlyPlayingMusicFilename();
14937   char *next_music = getMusicInfoEntryFilename(music_nr);
14938
14939   if (!strEqual(curr_music, next_music))
14940     FadeMusic();
14941 }
14942
14943 void FadeLevelSoundsAndMusic(void)
14944 {
14945   FadeLevelSounds();
14946   FadeLevelMusic();
14947 }
14948
14949 static void PlayLevelMusic(void)
14950 {
14951   int music_nr = getLevelMusicNr();
14952   char *curr_music = getCurrentlyPlayingMusicFilename();
14953   char *next_music = getMusicInfoEntryFilename(music_nr);
14954
14955   if (!strEqual(curr_music, next_music))
14956     PlayMusicLoop(music_nr);
14957 }
14958
14959 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14960 {
14961   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
14962   int offset = 0;
14963   int x = xx - offset;
14964   int y = yy - offset;
14965
14966   switch (sample)
14967   {
14968     case SOUND_blank:
14969       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14970       break;
14971
14972     case SOUND_roll:
14973       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14974       break;
14975
14976     case SOUND_stone:
14977       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14978       break;
14979
14980     case SOUND_nut:
14981       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14982       break;
14983
14984     case SOUND_crack:
14985       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14986       break;
14987
14988     case SOUND_bug:
14989       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14990       break;
14991
14992     case SOUND_tank:
14993       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14994       break;
14995
14996     case SOUND_android_clone:
14997       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14998       break;
14999
15000     case SOUND_android_move:
15001       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15002       break;
15003
15004     case SOUND_spring:
15005       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15006       break;
15007
15008     case SOUND_slurp:
15009       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15010       break;
15011
15012     case SOUND_eater:
15013       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15014       break;
15015
15016     case SOUND_eater_eat:
15017       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15018       break;
15019
15020     case SOUND_alien:
15021       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15022       break;
15023
15024     case SOUND_collect:
15025       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15026       break;
15027
15028     case SOUND_diamond:
15029       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15030       break;
15031
15032     case SOUND_squash:
15033       // !!! CHECK THIS !!!
15034 #if 1
15035       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15036 #else
15037       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15038 #endif
15039       break;
15040
15041     case SOUND_wonderfall:
15042       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15043       break;
15044
15045     case SOUND_drip:
15046       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15047       break;
15048
15049     case SOUND_push:
15050       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15051       break;
15052
15053     case SOUND_dirt:
15054       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15055       break;
15056
15057     case SOUND_acid:
15058       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15059       break;
15060
15061     case SOUND_ball:
15062       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15063       break;
15064
15065     case SOUND_slide:
15066       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15067       break;
15068
15069     case SOUND_wonder:
15070       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15071       break;
15072
15073     case SOUND_door:
15074       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15075       break;
15076
15077     case SOUND_exit_open:
15078       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15079       break;
15080
15081     case SOUND_exit_leave:
15082       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15083       break;
15084
15085     case SOUND_dynamite:
15086       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15087       break;
15088
15089     case SOUND_tick:
15090       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15091       break;
15092
15093     case SOUND_press:
15094       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15095       break;
15096
15097     case SOUND_wheel:
15098       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15099       break;
15100
15101     case SOUND_boom:
15102       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15103       break;
15104
15105     case SOUND_die:
15106       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15107       break;
15108
15109     case SOUND_time:
15110       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15111       break;
15112
15113     default:
15114       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15115       break;
15116   }
15117 }
15118
15119 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15120 {
15121   int element = map_element_SP_to_RND(element_sp);
15122   int action = map_action_SP_to_RND(action_sp);
15123   int offset = (setup.sp_show_border_elements ? 0 : 1);
15124   int x = xx - offset;
15125   int y = yy - offset;
15126
15127   PlayLevelSoundElementAction(x, y, element, action);
15128 }
15129
15130 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15131 {
15132   int element = map_element_MM_to_RND(element_mm);
15133   int action = map_action_MM_to_RND(action_mm);
15134   int offset = 0;
15135   int x = xx - offset;
15136   int y = yy - offset;
15137
15138   if (!IS_MM_ELEMENT(element))
15139     element = EL_MM_DEFAULT;
15140
15141   PlayLevelSoundElementAction(x, y, element, action);
15142 }
15143
15144 void PlaySound_MM(int sound_mm)
15145 {
15146   int sound = map_sound_MM_to_RND(sound_mm);
15147
15148   if (sound == SND_UNDEFINED)
15149     return;
15150
15151   PlaySound(sound);
15152 }
15153
15154 void PlaySoundLoop_MM(int sound_mm)
15155 {
15156   int sound = map_sound_MM_to_RND(sound_mm);
15157
15158   if (sound == SND_UNDEFINED)
15159     return;
15160
15161   PlaySoundLoop(sound);
15162 }
15163
15164 void StopSound_MM(int sound_mm)
15165 {
15166   int sound = map_sound_MM_to_RND(sound_mm);
15167
15168   if (sound == SND_UNDEFINED)
15169     return;
15170
15171   StopSound(sound);
15172 }
15173
15174 void RaiseScore(int value)
15175 {
15176   game.score += value;
15177
15178   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15179
15180   DisplayGameControlValues();
15181 }
15182
15183 void RaiseScoreElement(int element)
15184 {
15185   switch (element)
15186   {
15187     case EL_EMERALD:
15188     case EL_BD_DIAMOND:
15189     case EL_EMERALD_YELLOW:
15190     case EL_EMERALD_RED:
15191     case EL_EMERALD_PURPLE:
15192     case EL_SP_INFOTRON:
15193       RaiseScore(level.score[SC_EMERALD]);
15194       break;
15195     case EL_DIAMOND:
15196       RaiseScore(level.score[SC_DIAMOND]);
15197       break;
15198     case EL_CRYSTAL:
15199       RaiseScore(level.score[SC_CRYSTAL]);
15200       break;
15201     case EL_PEARL:
15202       RaiseScore(level.score[SC_PEARL]);
15203       break;
15204     case EL_BUG:
15205     case EL_BD_BUTTERFLY:
15206     case EL_SP_ELECTRON:
15207       RaiseScore(level.score[SC_BUG]);
15208       break;
15209     case EL_SPACESHIP:
15210     case EL_BD_FIREFLY:
15211     case EL_SP_SNIKSNAK:
15212       RaiseScore(level.score[SC_SPACESHIP]);
15213       break;
15214     case EL_YAMYAM:
15215     case EL_DARK_YAMYAM:
15216       RaiseScore(level.score[SC_YAMYAM]);
15217       break;
15218     case EL_ROBOT:
15219       RaiseScore(level.score[SC_ROBOT]);
15220       break;
15221     case EL_PACMAN:
15222       RaiseScore(level.score[SC_PACMAN]);
15223       break;
15224     case EL_NUT:
15225       RaiseScore(level.score[SC_NUT]);
15226       break;
15227     case EL_DYNAMITE:
15228     case EL_EM_DYNAMITE:
15229     case EL_SP_DISK_RED:
15230     case EL_DYNABOMB_INCREASE_NUMBER:
15231     case EL_DYNABOMB_INCREASE_SIZE:
15232     case EL_DYNABOMB_INCREASE_POWER:
15233       RaiseScore(level.score[SC_DYNAMITE]);
15234       break;
15235     case EL_SHIELD_NORMAL:
15236     case EL_SHIELD_DEADLY:
15237       RaiseScore(level.score[SC_SHIELD]);
15238       break;
15239     case EL_EXTRA_TIME:
15240       RaiseScore(level.extra_time_score);
15241       break;
15242     case EL_KEY_1:
15243     case EL_KEY_2:
15244     case EL_KEY_3:
15245     case EL_KEY_4:
15246     case EL_EM_KEY_1:
15247     case EL_EM_KEY_2:
15248     case EL_EM_KEY_3:
15249     case EL_EM_KEY_4:
15250     case EL_EMC_KEY_5:
15251     case EL_EMC_KEY_6:
15252     case EL_EMC_KEY_7:
15253     case EL_EMC_KEY_8:
15254     case EL_DC_KEY_WHITE:
15255       RaiseScore(level.score[SC_KEY]);
15256       break;
15257     default:
15258       RaiseScore(element_info[element].collect_score);
15259       break;
15260   }
15261 }
15262
15263 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15264 {
15265   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15266   {
15267     // closing door required in case of envelope style request dialogs
15268     if (!skip_request)
15269     {
15270       // prevent short reactivation of overlay buttons while closing door
15271       SetOverlayActive(FALSE);
15272
15273       CloseDoor(DOOR_CLOSE_1);
15274     }
15275
15276     if (network.enabled)
15277       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15278     else
15279     {
15280       if (quick_quit)
15281         FadeSkipNextFadeIn();
15282
15283       SetGameStatus(GAME_MODE_MAIN);
15284
15285       DrawMainMenu();
15286     }
15287   }
15288   else          // continue playing the game
15289   {
15290     if (tape.playing && tape.deactivate_display)
15291       TapeDeactivateDisplayOff(TRUE);
15292
15293     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15294
15295     if (tape.playing && tape.deactivate_display)
15296       TapeDeactivateDisplayOn();
15297   }
15298 }
15299
15300 void RequestQuitGame(boolean ask_if_really_quit)
15301 {
15302   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15303   boolean skip_request = game.all_players_gone || quick_quit;
15304
15305   RequestQuitGameExt(skip_request, quick_quit,
15306                      "Do you really want to quit the game?");
15307 }
15308
15309 void RequestRestartGame(char *message)
15310 {
15311   game.restart_game_message = NULL;
15312
15313   boolean has_started_game = hasStartedNetworkGame();
15314   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15315
15316   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15317   {
15318     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15319   }
15320   else
15321   {
15322     // needed in case of envelope request to close game panel
15323     CloseDoor(DOOR_CLOSE_1);
15324
15325     SetGameStatus(GAME_MODE_MAIN);
15326
15327     DrawMainMenu();
15328   }
15329 }
15330
15331 void CheckGameOver(void)
15332 {
15333   static boolean last_game_over = FALSE;
15334   static int game_over_delay = 0;
15335   int game_over_delay_value = 50;
15336   boolean game_over = checkGameFailed();
15337
15338   // do not handle game over if request dialog is already active
15339   if (game.request_active)
15340     return;
15341
15342   // do not ask to play again if game was never actually played
15343   if (!game.GamePlayed)
15344     return;
15345
15346   if (!game_over)
15347   {
15348     last_game_over = FALSE;
15349     game_over_delay = game_over_delay_value;
15350
15351     return;
15352   }
15353
15354   if (game_over_delay > 0)
15355   {
15356     game_over_delay--;
15357
15358     return;
15359   }
15360
15361   if (last_game_over != game_over)
15362     game.restart_game_message = (hasStartedNetworkGame() ?
15363                                  "Game over! Play it again?" :
15364                                  "Game over!");
15365
15366   last_game_over = game_over;
15367 }
15368
15369 boolean checkGameSolved(void)
15370 {
15371   // set for all game engines if level was solved
15372   return game.LevelSolved_GameEnd;
15373 }
15374
15375 boolean checkGameFailed(void)
15376 {
15377   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15378     return (game_em.game_over && !game_em.level_solved);
15379   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15380     return (game_sp.game_over && !game_sp.level_solved);
15381   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15382     return (game_mm.game_over && !game_mm.level_solved);
15383   else                          // GAME_ENGINE_TYPE_RND
15384     return (game.GameOver && !game.LevelSolved);
15385 }
15386
15387 boolean checkGameEnded(void)
15388 {
15389   return (checkGameSolved() || checkGameFailed());
15390 }
15391
15392
15393 // ----------------------------------------------------------------------------
15394 // random generator functions
15395 // ----------------------------------------------------------------------------
15396
15397 unsigned int InitEngineRandom_RND(int seed)
15398 {
15399   game.num_random_calls = 0;
15400
15401   return InitEngineRandom(seed);
15402 }
15403
15404 unsigned int RND(int max)
15405 {
15406   if (max > 0)
15407   {
15408     game.num_random_calls++;
15409
15410     return GetEngineRandom(max);
15411   }
15412
15413   return 0;
15414 }
15415
15416
15417 // ----------------------------------------------------------------------------
15418 // game engine snapshot handling functions
15419 // ----------------------------------------------------------------------------
15420
15421 struct EngineSnapshotInfo
15422 {
15423   // runtime values for custom element collect score
15424   int collect_score[NUM_CUSTOM_ELEMENTS];
15425
15426   // runtime values for group element choice position
15427   int choice_pos[NUM_GROUP_ELEMENTS];
15428
15429   // runtime values for belt position animations
15430   int belt_graphic[4][NUM_BELT_PARTS];
15431   int belt_anim_mode[4][NUM_BELT_PARTS];
15432 };
15433
15434 static struct EngineSnapshotInfo engine_snapshot_rnd;
15435 static char *snapshot_level_identifier = NULL;
15436 static int snapshot_level_nr = -1;
15437
15438 static void SaveEngineSnapshotValues_RND(void)
15439 {
15440   static int belt_base_active_element[4] =
15441   {
15442     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15443     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15444     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15445     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15446   };
15447   int i, j;
15448
15449   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15450   {
15451     int element = EL_CUSTOM_START + i;
15452
15453     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15454   }
15455
15456   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15457   {
15458     int element = EL_GROUP_START + i;
15459
15460     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15461   }
15462
15463   for (i = 0; i < 4; i++)
15464   {
15465     for (j = 0; j < NUM_BELT_PARTS; j++)
15466     {
15467       int element = belt_base_active_element[i] + j;
15468       int graphic = el2img(element);
15469       int anim_mode = graphic_info[graphic].anim_mode;
15470
15471       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15472       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15473     }
15474   }
15475 }
15476
15477 static void LoadEngineSnapshotValues_RND(void)
15478 {
15479   unsigned int num_random_calls = game.num_random_calls;
15480   int i, j;
15481
15482   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15483   {
15484     int element = EL_CUSTOM_START + i;
15485
15486     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15487   }
15488
15489   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15490   {
15491     int element = EL_GROUP_START + i;
15492
15493     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15494   }
15495
15496   for (i = 0; i < 4; i++)
15497   {
15498     for (j = 0; j < NUM_BELT_PARTS; j++)
15499     {
15500       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15501       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15502
15503       graphic_info[graphic].anim_mode = anim_mode;
15504     }
15505   }
15506
15507   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15508   {
15509     InitRND(tape.random_seed);
15510     for (i = 0; i < num_random_calls; i++)
15511       RND(1);
15512   }
15513
15514   if (game.num_random_calls != num_random_calls)
15515   {
15516     Error("number of random calls out of sync");
15517     Error("number of random calls should be %d", num_random_calls);
15518     Error("number of random calls is %d", game.num_random_calls);
15519
15520     Fail("this should not happen -- please debug");
15521   }
15522 }
15523
15524 void FreeEngineSnapshotSingle(void)
15525 {
15526   FreeSnapshotSingle();
15527
15528   setString(&snapshot_level_identifier, NULL);
15529   snapshot_level_nr = -1;
15530 }
15531
15532 void FreeEngineSnapshotList(void)
15533 {
15534   FreeSnapshotList();
15535 }
15536
15537 static ListNode *SaveEngineSnapshotBuffers(void)
15538 {
15539   ListNode *buffers = NULL;
15540
15541   // copy some special values to a structure better suited for the snapshot
15542
15543   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15544     SaveEngineSnapshotValues_RND();
15545   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15546     SaveEngineSnapshotValues_EM();
15547   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15548     SaveEngineSnapshotValues_SP(&buffers);
15549   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15550     SaveEngineSnapshotValues_MM(&buffers);
15551
15552   // save values stored in special snapshot structure
15553
15554   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15555     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15556   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15557     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15558   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15559     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15560   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15561     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15562
15563   // save further RND engine values
15564
15565   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15566   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15567   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15568
15569   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15570   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15571   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15572   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15573   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15574
15575   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15576   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15577   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15578
15579   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15580
15581   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15582   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15583
15584   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15585   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15586   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15587   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15588   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15589   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15590   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15591   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15592   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15593   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15594   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15595   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15596   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15597   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15598   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15599   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15600   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15601   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15602
15603   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15604   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15605
15606   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15607   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15608   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15609
15610   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15611   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15612
15613   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15614   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15615   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15616   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15617   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15618
15619   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15620   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15621
15622 #if 0
15623   ListNode *node = engine_snapshot_list_rnd;
15624   int num_bytes = 0;
15625
15626   while (node != NULL)
15627   {
15628     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15629
15630     node = node->next;
15631   }
15632
15633   Debug("game:playing:SaveEngineSnapshotBuffers",
15634         "size of engine snapshot: %d bytes", num_bytes);
15635 #endif
15636
15637   return buffers;
15638 }
15639
15640 void SaveEngineSnapshotSingle(void)
15641 {
15642   ListNode *buffers = SaveEngineSnapshotBuffers();
15643
15644   // finally save all snapshot buffers to single snapshot
15645   SaveSnapshotSingle(buffers);
15646
15647   // save level identification information
15648   setString(&snapshot_level_identifier, leveldir_current->identifier);
15649   snapshot_level_nr = level_nr;
15650 }
15651
15652 boolean CheckSaveEngineSnapshotToList(void)
15653 {
15654   boolean save_snapshot =
15655     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15656      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15657       game.snapshot.changed_action) ||
15658      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15659       game.snapshot.collected_item));
15660
15661   game.snapshot.changed_action = FALSE;
15662   game.snapshot.collected_item = FALSE;
15663   game.snapshot.save_snapshot = save_snapshot;
15664
15665   return save_snapshot;
15666 }
15667
15668 void SaveEngineSnapshotToList(void)
15669 {
15670   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15671       tape.quick_resume)
15672     return;
15673
15674   ListNode *buffers = SaveEngineSnapshotBuffers();
15675
15676   // finally save all snapshot buffers to snapshot list
15677   SaveSnapshotToList(buffers);
15678 }
15679
15680 void SaveEngineSnapshotToListInitial(void)
15681 {
15682   FreeEngineSnapshotList();
15683
15684   SaveEngineSnapshotToList();
15685 }
15686
15687 static void LoadEngineSnapshotValues(void)
15688 {
15689   // restore special values from snapshot structure
15690
15691   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15692     LoadEngineSnapshotValues_RND();
15693   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15694     LoadEngineSnapshotValues_EM();
15695   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15696     LoadEngineSnapshotValues_SP();
15697   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15698     LoadEngineSnapshotValues_MM();
15699 }
15700
15701 void LoadEngineSnapshotSingle(void)
15702 {
15703   LoadSnapshotSingle();
15704
15705   LoadEngineSnapshotValues();
15706 }
15707
15708 static void LoadEngineSnapshot_Undo(int steps)
15709 {
15710   LoadSnapshotFromList_Older(steps);
15711
15712   LoadEngineSnapshotValues();
15713 }
15714
15715 static void LoadEngineSnapshot_Redo(int steps)
15716 {
15717   LoadSnapshotFromList_Newer(steps);
15718
15719   LoadEngineSnapshotValues();
15720 }
15721
15722 boolean CheckEngineSnapshotSingle(void)
15723 {
15724   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15725           snapshot_level_nr == level_nr);
15726 }
15727
15728 boolean CheckEngineSnapshotList(void)
15729 {
15730   return CheckSnapshotList();
15731 }
15732
15733
15734 // ---------- new game button stuff -------------------------------------------
15735
15736 static struct
15737 {
15738   int graphic;
15739   struct XY *pos;
15740   int gadget_id;
15741   boolean *setup_value;
15742   boolean allowed_on_tape;
15743   boolean is_touch_button;
15744   char *infotext;
15745 } gamebutton_info[NUM_GAME_BUTTONS] =
15746 {
15747   {
15748     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15749     GAME_CTRL_ID_STOP,                          NULL,
15750     TRUE, FALSE,                                "stop game"
15751   },
15752   {
15753     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15754     GAME_CTRL_ID_PAUSE,                         NULL,
15755     TRUE, FALSE,                                "pause game"
15756   },
15757   {
15758     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15759     GAME_CTRL_ID_PLAY,                          NULL,
15760     TRUE, FALSE,                                "play game"
15761   },
15762   {
15763     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15764     GAME_CTRL_ID_UNDO,                          NULL,
15765     TRUE, FALSE,                                "undo step"
15766   },
15767   {
15768     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15769     GAME_CTRL_ID_REDO,                          NULL,
15770     TRUE, FALSE,                                "redo step"
15771   },
15772   {
15773     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15774     GAME_CTRL_ID_SAVE,                          NULL,
15775     TRUE, FALSE,                                "save game"
15776   },
15777   {
15778     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15779     GAME_CTRL_ID_PAUSE2,                        NULL,
15780     TRUE, FALSE,                                "pause game"
15781   },
15782   {
15783     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15784     GAME_CTRL_ID_LOAD,                          NULL,
15785     TRUE, FALSE,                                "load game"
15786   },
15787   {
15788     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15789     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15790     FALSE, FALSE,                               "stop game"
15791   },
15792   {
15793     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15794     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15795     FALSE, FALSE,                               "pause game"
15796   },
15797   {
15798     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15799     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15800     FALSE, FALSE,                               "play game"
15801   },
15802   {
15803     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
15804     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
15805     FALSE, TRUE,                                "stop game"
15806   },
15807   {
15808     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
15809     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
15810     FALSE, TRUE,                                "pause game"
15811   },
15812   {
15813     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15814     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15815     TRUE, FALSE,                                "background music on/off"
15816   },
15817   {
15818     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15819     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15820     TRUE, FALSE,                                "sound loops on/off"
15821   },
15822   {
15823     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15824     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15825     TRUE, FALSE,                                "normal sounds on/off"
15826   },
15827   {
15828     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15829     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15830     FALSE, FALSE,                               "background music on/off"
15831   },
15832   {
15833     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15834     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15835     FALSE, FALSE,                               "sound loops on/off"
15836   },
15837   {
15838     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15839     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15840     FALSE, FALSE,                               "normal sounds on/off"
15841   }
15842 };
15843
15844 void CreateGameButtons(void)
15845 {
15846   int i;
15847
15848   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15849   {
15850     int graphic = gamebutton_info[i].graphic;
15851     struct GraphicInfo *gfx = &graphic_info[graphic];
15852     struct XY *pos = gamebutton_info[i].pos;
15853     struct GadgetInfo *gi;
15854     int button_type;
15855     boolean checked;
15856     unsigned int event_mask;
15857     boolean is_touch_button = gamebutton_info[i].is_touch_button;
15858     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15859     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15860     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
15861     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
15862     int gd_x   = gfx->src_x;
15863     int gd_y   = gfx->src_y;
15864     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15865     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15866     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15867     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15868     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15869     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15870     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
15871     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
15872     int id = i;
15873
15874     if (gfx->bitmap == NULL)
15875     {
15876       game_gadget[id] = NULL;
15877
15878       continue;
15879     }
15880
15881     if (id == GAME_CTRL_ID_STOP ||
15882         id == GAME_CTRL_ID_PANEL_STOP ||
15883         id == GAME_CTRL_ID_TOUCH_STOP ||
15884         id == GAME_CTRL_ID_PLAY ||
15885         id == GAME_CTRL_ID_PANEL_PLAY ||
15886         id == GAME_CTRL_ID_SAVE ||
15887         id == GAME_CTRL_ID_LOAD)
15888     {
15889       button_type = GD_TYPE_NORMAL_BUTTON;
15890       checked = FALSE;
15891       event_mask = GD_EVENT_RELEASED;
15892     }
15893     else if (id == GAME_CTRL_ID_UNDO ||
15894              id == GAME_CTRL_ID_REDO)
15895     {
15896       button_type = GD_TYPE_NORMAL_BUTTON;
15897       checked = FALSE;
15898       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15899     }
15900     else
15901     {
15902       button_type = GD_TYPE_CHECK_BUTTON;
15903       checked = (gamebutton_info[i].setup_value != NULL ?
15904                  *gamebutton_info[i].setup_value : FALSE);
15905       event_mask = GD_EVENT_PRESSED;
15906     }
15907
15908     gi = CreateGadget(GDI_CUSTOM_ID, id,
15909                       GDI_IMAGE_ID, graphic,
15910                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15911                       GDI_X, base_x + x,
15912                       GDI_Y, base_y + y,
15913                       GDI_WIDTH, gfx->width,
15914                       GDI_HEIGHT, gfx->height,
15915                       GDI_TYPE, button_type,
15916                       GDI_STATE, GD_BUTTON_UNPRESSED,
15917                       GDI_CHECKED, checked,
15918                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15919                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15920                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15921                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15922                       GDI_DIRECT_DRAW, FALSE,
15923                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
15924                       GDI_EVENT_MASK, event_mask,
15925                       GDI_CALLBACK_ACTION, HandleGameButtons,
15926                       GDI_END);
15927
15928     if (gi == NULL)
15929       Fail("cannot create gadget");
15930
15931     game_gadget[id] = gi;
15932   }
15933 }
15934
15935 void FreeGameButtons(void)
15936 {
15937   int i;
15938
15939   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15940     FreeGadget(game_gadget[i]);
15941 }
15942
15943 static void UnmapGameButtonsAtSamePosition(int id)
15944 {
15945   int i;
15946
15947   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15948     if (i != id &&
15949         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15950         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15951       UnmapGadget(game_gadget[i]);
15952 }
15953
15954 static void UnmapGameButtonsAtSamePosition_All(void)
15955 {
15956   if (setup.show_snapshot_buttons)
15957   {
15958     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15959     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15960     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15961   }
15962   else
15963   {
15964     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15965     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15966     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15967
15968     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15969     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15970     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15971   }
15972 }
15973
15974 static void MapGameButtonsAtSamePosition(int id)
15975 {
15976   int i;
15977
15978   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15979     if (i != id &&
15980         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15981         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15982       MapGadget(game_gadget[i]);
15983
15984   UnmapGameButtonsAtSamePosition_All();
15985 }
15986
15987 void MapUndoRedoButtons(void)
15988 {
15989   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15990   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15991
15992   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15993   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15994 }
15995
15996 void UnmapUndoRedoButtons(void)
15997 {
15998   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15999   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16000
16001   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16002   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16003 }
16004
16005 void ModifyPauseButtons(void)
16006 {
16007   static int ids[] =
16008   {
16009     GAME_CTRL_ID_PAUSE,
16010     GAME_CTRL_ID_PAUSE2,
16011     GAME_CTRL_ID_PANEL_PAUSE,
16012     GAME_CTRL_ID_TOUCH_PAUSE,
16013     -1
16014   };
16015   int i;
16016
16017   for (i = 0; ids[i] > -1; i++)
16018     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16019 }
16020
16021 static void MapGameButtonsExt(boolean on_tape)
16022 {
16023   int i;
16024
16025   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16026     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
16027         i != GAME_CTRL_ID_UNDO &&
16028         i != GAME_CTRL_ID_REDO)
16029       MapGadget(game_gadget[i]);
16030
16031   UnmapGameButtonsAtSamePosition_All();
16032
16033   RedrawGameButtons();
16034 }
16035
16036 static void UnmapGameButtonsExt(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       UnmapGadget(game_gadget[i]);
16043 }
16044
16045 static void RedrawGameButtonsExt(boolean on_tape)
16046 {
16047   int i;
16048
16049   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16050     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16051       RedrawGadget(game_gadget[i]);
16052 }
16053
16054 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16055 {
16056   if (gi == NULL)
16057     return;
16058
16059   gi->checked = state;
16060 }
16061
16062 static void RedrawSoundButtonGadget(int id)
16063 {
16064   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16065              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16066              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16067              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16068              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16069              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16070              id);
16071
16072   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16073   RedrawGadget(game_gadget[id2]);
16074 }
16075
16076 void MapGameButtons(void)
16077 {
16078   MapGameButtonsExt(FALSE);
16079 }
16080
16081 void UnmapGameButtons(void)
16082 {
16083   UnmapGameButtonsExt(FALSE);
16084 }
16085
16086 void RedrawGameButtons(void)
16087 {
16088   RedrawGameButtonsExt(FALSE);
16089 }
16090
16091 void MapGameButtonsOnTape(void)
16092 {
16093   MapGameButtonsExt(TRUE);
16094 }
16095
16096 void UnmapGameButtonsOnTape(void)
16097 {
16098   UnmapGameButtonsExt(TRUE);
16099 }
16100
16101 void RedrawGameButtonsOnTape(void)
16102 {
16103   RedrawGameButtonsExt(TRUE);
16104 }
16105
16106 static void GameUndoRedoExt(void)
16107 {
16108   ClearPlayerAction();
16109
16110   tape.pausing = TRUE;
16111
16112   RedrawPlayfield();
16113   UpdateAndDisplayGameControlValues();
16114
16115   DrawCompleteVideoDisplay();
16116   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16117   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16118   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16119
16120   BackToFront();
16121 }
16122
16123 static void GameUndo(int steps)
16124 {
16125   if (!CheckEngineSnapshotList())
16126     return;
16127
16128   LoadEngineSnapshot_Undo(steps);
16129
16130   GameUndoRedoExt();
16131 }
16132
16133 static void GameRedo(int steps)
16134 {
16135   if (!CheckEngineSnapshotList())
16136     return;
16137
16138   LoadEngineSnapshot_Redo(steps);
16139
16140   GameUndoRedoExt();
16141 }
16142
16143 static void HandleGameButtonsExt(int id, int button)
16144 {
16145   static boolean game_undo_executed = FALSE;
16146   int steps = BUTTON_STEPSIZE(button);
16147   boolean handle_game_buttons =
16148     (game_status == GAME_MODE_PLAYING ||
16149      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16150
16151   if (!handle_game_buttons)
16152     return;
16153
16154   switch (id)
16155   {
16156     case GAME_CTRL_ID_STOP:
16157     case GAME_CTRL_ID_PANEL_STOP:
16158     case GAME_CTRL_ID_TOUCH_STOP:
16159       if (game_status == GAME_MODE_MAIN)
16160         break;
16161
16162       if (tape.playing)
16163         TapeStop();
16164       else
16165         RequestQuitGame(TRUE);
16166
16167       break;
16168
16169     case GAME_CTRL_ID_PAUSE:
16170     case GAME_CTRL_ID_PAUSE2:
16171     case GAME_CTRL_ID_PANEL_PAUSE:
16172     case GAME_CTRL_ID_TOUCH_PAUSE:
16173       if (network.enabled && game_status == GAME_MODE_PLAYING)
16174       {
16175         if (tape.pausing)
16176           SendToServer_ContinuePlaying();
16177         else
16178           SendToServer_PausePlaying();
16179       }
16180       else
16181         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16182
16183       game_undo_executed = FALSE;
16184
16185       break;
16186
16187     case GAME_CTRL_ID_PLAY:
16188     case GAME_CTRL_ID_PANEL_PLAY:
16189       if (game_status == GAME_MODE_MAIN)
16190       {
16191         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16192       }
16193       else if (tape.pausing)
16194       {
16195         if (network.enabled)
16196           SendToServer_ContinuePlaying();
16197         else
16198           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16199       }
16200       break;
16201
16202     case GAME_CTRL_ID_UNDO:
16203       // Important: When using "save snapshot when collecting an item" mode,
16204       // load last (current) snapshot for first "undo" after pressing "pause"
16205       // (else the last-but-one snapshot would be loaded, because the snapshot
16206       // pointer already points to the last snapshot when pressing "pause",
16207       // which is fine for "every step/move" mode, but not for "every collect")
16208       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16209           !game_undo_executed)
16210         steps--;
16211
16212       game_undo_executed = TRUE;
16213
16214       GameUndo(steps);
16215       break;
16216
16217     case GAME_CTRL_ID_REDO:
16218       GameRedo(steps);
16219       break;
16220
16221     case GAME_CTRL_ID_SAVE:
16222       TapeQuickSave();
16223       break;
16224
16225     case GAME_CTRL_ID_LOAD:
16226       TapeQuickLoad();
16227       break;
16228
16229     case SOUND_CTRL_ID_MUSIC:
16230     case SOUND_CTRL_ID_PANEL_MUSIC:
16231       if (setup.sound_music)
16232       { 
16233         setup.sound_music = FALSE;
16234
16235         FadeMusic();
16236       }
16237       else if (audio.music_available)
16238       { 
16239         setup.sound = setup.sound_music = TRUE;
16240
16241         SetAudioMode(setup.sound);
16242
16243         if (game_status == GAME_MODE_PLAYING)
16244           PlayLevelMusic();
16245       }
16246
16247       RedrawSoundButtonGadget(id);
16248
16249       break;
16250
16251     case SOUND_CTRL_ID_LOOPS:
16252     case SOUND_CTRL_ID_PANEL_LOOPS:
16253       if (setup.sound_loops)
16254         setup.sound_loops = FALSE;
16255       else if (audio.loops_available)
16256       {
16257         setup.sound = setup.sound_loops = TRUE;
16258
16259         SetAudioMode(setup.sound);
16260       }
16261
16262       RedrawSoundButtonGadget(id);
16263
16264       break;
16265
16266     case SOUND_CTRL_ID_SIMPLE:
16267     case SOUND_CTRL_ID_PANEL_SIMPLE:
16268       if (setup.sound_simple)
16269         setup.sound_simple = FALSE;
16270       else if (audio.sound_available)
16271       {
16272         setup.sound = setup.sound_simple = TRUE;
16273
16274         SetAudioMode(setup.sound);
16275       }
16276
16277       RedrawSoundButtonGadget(id);
16278
16279       break;
16280
16281     default:
16282       break;
16283   }
16284 }
16285
16286 static void HandleGameButtons(struct GadgetInfo *gi)
16287 {
16288   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16289 }
16290
16291 void HandleSoundButtonKeys(Key key)
16292 {
16293   if (key == setup.shortcut.sound_simple)
16294     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16295   else if (key == setup.shortcut.sound_loops)
16296     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16297   else if (key == setup.shortcut.sound_music)
16298     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16299 }